

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/blog/img/fluid.png">
  <link rel="icon" href="/blog/img/avatar2.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Zhou Jie">
  <meta name="keywords" content="">
  
    <meta name="description" content="CSS1.说一下CSS盒模型 是什么：在HTML上任何一个元素都可以看成一个CSS盒模型 有什么：对于标准盒模型来说，包含四个属性：margin、border、padding、content ​               但是对于IE盒模型来说，只有两个属性：margin、content（已包含border、padding） 对于有什么如何区分：通过属性box-sizing来设置两种模型:con">
<meta property="og:type" content="article">
<meta property="og:title" content="前端高频面试题-基础">
<meta property="og:url" content="https://jay213.gitee.io/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/index.html">
<meta property="og:site_name" content="Zhou Jie">
<meta property="og:description" content="CSS1.说一下CSS盒模型 是什么：在HTML上任何一个元素都可以看成一个CSS盒模型 有什么：对于标准盒模型来说，包含四个属性：margin、border、padding、content ​               但是对于IE盒模型来说，只有两个属性：margin、content（已包含border、padding） 对于有什么如何区分：通过属性box-sizing来设置两种模型:con">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://jay213.gitee.io/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/%E5%8E%9F%E5%9E%8B%E9%93%BE.png">
<meta property="og:image" content="https://jay213.gitee.io/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/%E7%BB%84%E4%BB%B6%E5%AE%9E%E4%BE%8B%E4%B8%8EVue%E5%AE%9E%E4%BE%8B%E5%85%B3%E7%B3%BB.png">
<meta property="article:published_time" content="2024-01-03T06:21:01.000Z">
<meta property="article:modified_time" content="2024-03-28T06:41:17.385Z">
<meta property="article:author" content="Zhou Jie">
<meta property="article:tag" content="前端综合">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://jay213.gitee.io/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/%E5%8E%9F%E5%9E%8B%E9%93%BE.png">
  
  
  
  <title>前端高频面试题-基础 - Zhou Jie</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/blog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/blog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/blog/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"jay213.gitee.io","root":"/blog/","version":"1.9.4","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/blog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/blog/js/utils.js" ></script>
  <script  src="/blog/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 5.4.2"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/blog/">
      <strong>Indispensible</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/about/">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/blog/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="前端高频面试题-基础"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2024-01-03 14:21" pubdate>
          2024年1月3日 下午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          51k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          425 分钟
        
      </span>
    

    
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">前端高频面试题-基础</h1>
            
            
              <div class="markdown-body">
                
                <h1 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h1><h3 id="1-说一下CSS盒模型"><a href="#1-说一下CSS盒模型" class="headerlink" title="1.说一下CSS盒模型"></a><strong>1.说一下CSS盒模型</strong></h3><blockquote>
<p>是什么：在HTML上任何一个元素都可以看成一个CSS盒模型</p>
<p>有什么：对于标准盒模型来说，包含四个属性：margin、border、padding、content</p>
<p>​               但是对于IE盒模型来说，只有两个属性：margin、content（已包含border、padding）</p>
<p>对于有什么如何区分：通过属性box-sizing来设置两种模型:content-box-&gt;标准盒模型</p>
<p>​                                                                                                    border-box-&gt;IE盒模型</p>
</blockquote>
<h3 id="2-CSS选择器的优先级"><a href="#2-CSS选择器的优先级" class="headerlink" title="2.CSS选择器的优先级"></a>2.CSS选择器的优先级</h3><blockquote>
<p>是什么：它是CSS三大特性之一，三大特性是：继承性、重叠性（相同优先级选择哪个）、优先级</p>
<p>（如何记忆三大特性？继承性和优先级都可能涉及重叠性，即后设置的都会覆盖先设置的，继承性不用选择直接拿来用，优先级还要选一下，那就这么记：重叠性-&gt;继承性-&gt;优先级）</p>
<p>有什么：!important（强制生效，可修改默认样式）&gt; 行内标签 &gt; id选择器 &gt; 类选择器（包括 属性值类选择器：[class=’属性名’]；*[class^=’prefix-‘],=，*是通配符，^=表时匹配前缀） &gt; 标签选择器 &gt; 全局选择器（*{}）</p>
</blockquote>
<h3 id="3-隐藏元素的方法有哪些？"><a href="#3-隐藏元素的方法有哪些？" class="headerlink" title="3.隐藏元素的方法有哪些？"></a>3.隐藏元素的方法有哪些？</h3><blockquote>
<ol>
<li>display:none（不占空间，彻底消除）</li>
<li>visibility:hidden或opacity:0（占空间，仅仅是视觉上看不到）</li>
<li>clip-path裁剪（占空间，裁剪时相当于复制一份裁再展示）</li>
<li>绝对定位，比如 left: -9999px; （脱离文档流，不占空间）</li>
</ol>
</blockquote>
<h3 id="4-px和rem的区别是什么？"><a href="#4-px和rem的区别是什么？" class="headerlink" title="4.px和rem的区别是什么？"></a>4.px和rem的区别是什么？</h3><blockquote>
<p>px是指像素，而rem的全称是”root em”，root是指它是以html为参照物的，em是另外一个单位，它是以父元素的字体大小为参照物，如果默认父元素fontsize:1px，1em = 16px，父元素变它就变，综上所述： rem是指以html的font-size为参照物的大小单位，html的font-size大，1rem就大</p>
<p>使用em或rem单位的好处是什么？</p>
<p>可以用在多个地方需要相同字体大小的场景，而且便于维护：想要一起变，只要改基准的font-size即可</p>
</blockquote>
<h3 id="5-重绘和重排有什么区别？"><a href="#5-重绘和重排有什么区别？" class="headerlink" title="5.重绘和重排有什么区别？"></a>5.重绘和重排有什么区别？</h3><blockquote>
<p>重排（回流）是指 元素的布局，例如位置和大小，发生变化后元素的重新排列（元素的删除添加、用js改变大小）</p>
<p>重绘是指 元素的颜色等发生变化的重新绘制</p>
<p>重绘和重排涉及到浏览器渲染机制，可以说下吗？</p>
<ul>
<li>解析HTML生成DOM树</li>
<li>同时解析CSS样式生成CSSOM树</li>
<li>解析js，比如事件等</li>
<li>将DOM树和CSSOM树合并成渲染树（render tree）</li>
<li>触发 重排-&gt;重绘-&gt;展示页面</li>
</ul>
</blockquote>
<h3 id="6-无宽高让一个元素水平垂直居中的方式有哪些？"><a href="#6-无宽高让一个元素水平垂直居中的方式有哪些？" class="headerlink" title="6.无宽高让一个元素水平垂直居中的方式有哪些？"></a>6.无宽高让一个元素水平垂直居中的方式有哪些？</h3><blockquote>
<ol>
<li><p>flex或者grid（为什么放在一起，首先是最常用的，而且使用起来几乎一样，grid仅仅是just-content换成了just-item，且都设置为center）</p>
</li>
<li><p>绝对定位+transform:tranlate(-50%,-50%)</p>
</li>
<li><p>绝对定位+margin（使用时）</p>
<p>父元素相对定位，子元素绝对定位，并且把 上下左右设置为0（别影响margin的使用），margin:auto（可以放开手脚）</p>
</li>
</ol>
<p>直接记 无宽高的，这样不管问到有没有宽高的，都回答这个</p>
</blockquote>
<h3 id="7-CSS中哪些元素可以继承？哪些不可以？"><a href="#7-CSS中哪些元素可以继承？哪些不可以？" class="headerlink" title="7.CSS中哪些元素可以继承？哪些不可以？"></a>7.CSS中哪些元素可以继承？哪些不可以？</h3><blockquote>
<p>以下属性可以继承，其他的基本不可以：</p>
<ol>
<li>字体类属性：font-size</li>
<li>文字类属性：line-height</li>
<li>可见性属性：visibility:hidden</li>
<li>列表类属性：list-style</li>
<li>表格类属性：border-color</li>
</ol>
<p>为什么，如何记忆？这些文字、用于展示文字的列表、表格 都是最常用的，常用的都有继承性，不太常用的就不需要有继承性</p>
</blockquote>
<h3 id="8-有没有用过预处理器？"><a href="#8-有没有用过预处理器？" class="headerlink" title="8.有没有用过预处理器？"></a>8.有没有用过预处理器？</h3><blockquote>
<p>是什么：它们是一种工具，在CSS的基础上增加了一些新的特性，比如变量、混入以及导入等，增强了样式的可维护性、可重用性以及可拓展性</p>
<p>有什么以及区别：有sass、scss、less三种，下面说一下它们的区别，除了上面说的特性以外，sass只支持严格的缩进写法，而scss、less支持大括号；而less相对于scss又更加简单直观，比如去除掉了scss中混入的使用的写法（原来在scss混入需要在前面加@minin，然后再用@include引入，less全部去掉了）</p>
</blockquote>
<h3 id="9-精灵图和base64的区别是什么？"><a href="#9-精灵图和base64的区别是什么？" class="headerlink" title="9.精灵图和base64的区别是什么？"></a>9.精灵图和base64的区别是什么？</h3><blockquote>
<p>精灵图：它可以利用 将多个图片合并成一张图片 的特性，从而减少图片HTTP的请求，达到提高页面加载性能的目的，但是不便于维护（图片的修改需要重新生成精灵图）</p>
<p>base64：它可以将图片的二进制转化为64位的字符串，随着页面的渲染一同渲染不需要额外的HTTP请求，也提高了页面加载性能，并解决了图片跨域问题，但是增加了文件体积</p>
<p>使用场景：</p>
<p>精灵图：适合图片较多且图片不用修改的场景</p>
<p>base64：适合图片较少的场景</p>
<p>如何实现的？（原理）</p>
<p>精灵图：合并图只能通过自己编辑合并，然后就是通过图片的background-position修改需要展示哪个小图片</p>
<p>base64：可通过canvas的toDataURL方法将图片转换为64位字符串</p>
<p><u>回答A与B区别的模板：</u></p>
<p><u>A是什么，有什么优点、缺点</u></p>
<p><u>B是什么，有什么优点、缺点</u></p>
<p><u>根据 各自 优缺点，回答 使用场景</u></p>
<p><u>往深处挖掘：各自实现的原理是什么？</u></p>
</blockquote>
<h3 id="10-canvas是什么？"><a href="#10-canvas是什么？" class="headerlink" title="10.canvas是什么？"></a>10.canvas是什么？</h3><blockquote>
<p>canvas是一个可编程的画布，通过js代码可以绘制图形、处理图像以及渲染动画等</p>
</blockquote>
<h3 id="11-svg格式了解多少？"><a href="#11-svg格式了解多少？" class="headerlink" title="11.svg格式了解多少？"></a>11.svg格式了解多少？</h3><blockquote>
<p>优点：它内部是基于数学矢量的计算（内部实现）—-&gt;可缩放还不失真、体积小（不像像素图象还要存储颜色信息，它是渲染时才绘制颜色）、下载速度快</p>
<p>​           它是基于XML的文本（实际存储形式）—-&gt;可被搜索</p>
<p>​           可以转化为base64来引入（如何使用）</p>
</blockquote>
<h3 id="12-H5C3（HTML5、CSS3）的语义化的理解"><a href="#12-H5C3（HTML5、CSS3）的语义化的理解" class="headerlink" title="12.H5C3（HTML5、CSS3）的语义化的理解"></a>12.H5C3（HTML5、CSS3）的语义化的理解</h3><blockquote>
<p>就是在写 HTML标签时，规范使用如下标签：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span><br> ...<br><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">aside</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span><br>&lt;footer...<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span><br> ...<br><span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>有如下好处：</p>
<p>对于程序员来说：易读、易于理解，开发者一起便于维护，提高开发效率</p>
<p>对于搜索引擎来说：可以提高搜索引擎优化（SEO）效果</p>
</blockquote>
<h3 id="13-H5C3有哪些新特性？"><a href="#13-H5C3有哪些新特性？" class="headerlink" title="13.H5C3有哪些新特性？"></a>13.H5C3有哪些新特性？</h3><blockquote>
<p>HTML5：思路-&gt;从语义化标签入手，每个语义化里面可能会装什么内容-&gt;header-&gt;aside-&gt;main</p>
<ul>
<li>语义化标签</li>
<li>本地存储（放在header中的个人头像信息，需要从这获取）</li>
<li>音频标签video（放在header中的背景音乐，可以切换音乐）</li>
<li>浏览器提供了 地理位置 API（aside中可作为选项供用户查看）</li>
<li>画布canvas（main中显示可视化图形，类似echarts）</li>
</ul>
<p>CSS3：思路-&gt;选择（选择器）哪个盒子（模型）添加  样式-&gt;布局、边框 包围-&gt;动画</p>
<ul>
<li><p>盒子模型</p>
</li>
<li><p>选择器</p>
</li>
<li><p>flex布局（水平垂直居中）</p>
</li>
<li><p>边框、阴影效果（阴影盒子）</p>
</li>
<li><p>动画效果（盒子内部有什么动画–鸡你太美）</p>
<p>即 一个带阴影的盒子内部有个鸡你太美的动画</p>
</li>
</ul>
</blockquote>
<h3 id="14-rem是如何做到适配的？"><a href="#14-rem是如何做到适配的？" class="headerlink" title="14.rem是如何做到适配的？"></a>14.rem是如何做到适配的？</h3><blockquote>
<p>利用rem根据html的font-size大小作为基准为单位的特性。比如 移动端屏幕宽度（或高度）不一样，那么我就可以 把html的font-size大小设置为 当前屏幕的宽度，这样在 当前屏幕的div大小，就可以用rem表示（0.5rem屏幕的一半），这样不管什么宽度的屏幕，都可以设置成 屏幕的一半宽度（例子）。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs js">&lt;!-- em是针对父元素的适配单位，而rem是只针对<span class="hljs-title function_">html</span>(root即根部)的适配单位<br>    比如html的fontSize为50px，然后body的字体设置为<span class="hljs-number">50</span>%，那么这个1em/1rem就为25px<br>--&gt;<br><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="language-css"><span class="language-xml">  <span class="hljs-selector-id">#box</span>&#123;</span></span><br><span class="language-css"><span class="language-xml">     <span class="hljs-attribute">width</span>: <span class="hljs-number">0.5rem</span>;</span></span><br><span class="language-css"><span class="language-xml">     <span class="hljs-attribute">height</span>: <span class="hljs-number">0.5rem</span>;</span></span><br><span class="language-css"><span class="language-xml">     <span class="hljs-attribute">background-color</span>: red;</span></span><br><span class="language-css"><span class="language-xml">  &#125;</span></span><br><span class="language-css"><span class="language-xml"></span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span><br>&lt;/head&gt;<br><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span></span><br><span class="language-xml"><span class="hljs-comment">&lt;!-- 方法就是 html根元素的字体大小 = 屏幕区的宽</span></span><br><span class="hljs-comment"><span class="language-xml">    目的是想把 1rem设置成整个屏幕的宽，那下次某个盒子想要设置成占整个屏幕多大比例，那直接 1rem/x</span></span><br><span class="hljs-comment"><span class="language-xml">    比如 某个盒子想要占屏幕的一半，那设置完之后就可以设置宽高为0.5rem-&gt;特指整个屏幕的一半，这样不管是什么分辨率都可以设置 </span></span><br><span class="hljs-comment"><span class="language-xml">    Ps：这里的字体大小默认是 指的是rem或em的单位</span></span><br><span class="hljs-comment"><span class="language-xml">   --&gt;</span></span><br><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;box&quot;</span>&gt;</span></span><br><span class="language-xml">    </span><br><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span><br><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span></span><br><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&#x27;text/javascript&#x27;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="language-javascript"><span class="language-xml"> <span class="hljs-variable language_">window</span>.<span class="hljs-property">onload</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">     <span class="hljs-comment">// 整个屏幕的宽</span></span></span><br><span class="language-javascript"><span class="language-xml">     <span class="hljs-keyword">var</span> width = <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">clientWidth</span></span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">     <span class="hljs-keyword">var</span> htmlNode = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;html&#x27;</span>)</span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">     htmlNode.<span class="hljs-property">style</span>.<span class="hljs-property">fontSize</span> = width + <span class="hljs-string">&#x27;px&#x27;</span></span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">     <span class="hljs-comment">// 以上三个步骤是为了当前屏幕单位 都为1rem</span></span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml"> &#125;</span></span><br><span class="language-javascript"><span class="language-xml"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br></code></pre></td></tr></table></figure>
</blockquote>
<h3 id="15-解决了哪些移动端兼容问题？"><a href="#15-解决了哪些移动端兼容问题？" class="headerlink" title="15.解决了哪些移动端兼容问题？"></a>15.解决了哪些移动端兼容问题？</h3><blockquote>
<ol>
<li><p>安卓环境input框的placeholder文字设置行高（为了垂直居中）之后会偏上</p>
<p>解决方法：input框有placeholder属性时不要设置行高，安卓环境可能自己默认样式就是偏高的</p>
<p>解决方法：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">autocorrect</span>=<span class="hljs-string">&#x27;off&#x27;</span> <span class="hljs-attr">autocapitalize</span>=<span class="hljs-string">&#x27;off&#x27;</span> /&gt;</span><br></code></pre></td></tr></table></figure></li>
<li><p>移动端在字体小于12px时，会显示异常（模糊、不清晰）</p>
<p>解决方法：使用transform先放大一倍，再缩小一倍。这边看似什么也没做，但里面涉及到了字体渲染时会用到的技术：抗锯齿化技术，在字体渲染到页面上之后字体的边缘是会呈现锯齿状的，因此在每次字体渲染时会使用抗锯齿化技术使得字体边缘变得平滑，这里先放大再缩小等于 重新抗锯齿化 一次。</p>
</li>
<li><p>IOS在input框设置disabled之后字体显示异常（比较灰暗）</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs css">  <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=button]</span>&#123;<br>    <span class="hljs-attribute">opacity</span>:<span class="hljs-number">1</span>// 把字体显示正常:不透明度=&gt;<span class="hljs-number">1</span><br>&#125;<br></code></pre></td></tr></table></figure></li>
<li><p>IOS在设置 overflow:auto/scroll 添加滚动条滑动时，会卡顿，就是 屏幕跟不上手动滑动的频率。</p>
<p>-webkit-overflow-scrolling: touch —————— 内置滚动改为触摸事件</p>
</li>
<li><p>IOS在填写文本时，碰到数字会自动识别成电话号码</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&#x27;format-detection&#x27;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&#x27;telephone=no&#x27;</span> /&gt;</span><br></code></pre></td></tr></table></figure></li>
</ol>
<p>​          meta：元标签，用来全局设置一些东西的，name设置什么，content内容是什么</p>
<p><u>思路</u>：登陆页面填写input（前-中【异常用小字体检验】-后）-&gt;登陆后划到最底端进行评论</p>
</blockquote>
<h3 id="16-display-none-、-visibility-hidden-和-opacity-0-之间的区别？"><a href="#16-display-none-、-visibility-hidden-和-opacity-0-之间的区别？" class="headerlink" title="16.display:none 、 visibility:hidden 和 opacity:0 之间的区别？"></a>16.display:none 、 visibility:hidden 和 opacity:0 之间的区别？</h3><blockquote>
<ul>
<li><p>是否占空间</p>
<p>display:none不占</p>
<p>visibility:hidden占</p>
<p>opacity:0占</p>
</li>
<li><p>是否继承</p>
<p>display:none无法继承（父元素都没了）</p>
<p>visibility:hidden可以继承（通过设置子元素 visibility:visible 来显示子元素）</p>
<p>opacity:0可以继承（ <u>但是  不能设置子元素 opacity:0 来先重新显示</u>）</p>
</li>
<li><p>事件绑定</p>
<p>display:none无法触发</p>
<p>visibility:hidden无法触发</p>
<p>opacity:0可以触发</p>
</li>
<li><p>动画</p>
<p>display:none无法触发transition</p>
<p>visibility:hidden无法触发transition</p>
<p>opacity:0可以触发transition</p>
</li>
</ul>
<p><u>思路</u>：都是指看不见，display:none啥都没了不谈了，另外两个 看不到又占空间 还不如继承给别人用（子元素可能会有要显示的需求）呢，在真正用的时候，一个是手动使用（事件），另一个是自动使用（动画）</p>
<p>程度：display（直接无了） &gt; visibility（功能性失效） &gt; opacity（继承很特殊）</p>
</blockquote>
<h3 id="17-弹性布局flex1-0增加了那些属性？"><a href="#17-弹性布局flex1-0增加了那些属性？" class="headerlink" title="17.弹性布局flex1.0增加了那些属性？"></a>17.弹性布局flex1.0增加了那些属性？</h3><blockquote>
<p>gap：弹性布局元素之间的间距</p>
<p>flex-basis：主轴方向各元素的初始大小</p>
</blockquote>
<h3 id="18-数据大屏适配方案有哪些？"><a href="#18-数据大屏适配方案有哪些？" class="headerlink" title="18.数据大屏适配方案有哪些？"></a>18.数据大屏适配方案有哪些？</h3><blockquote>
<p>常用的是以下两种：</p>
<ol>
<li>vw和vh（和下面的rem类似）：<ul>
<li>是什么：它是只视口的宽高，就是不管屏幕尺寸多少，那么100vh和100vw那就是占满整个屏幕的宽高的</li>
<li>如何适配：利用实际屏幕和UI稿的大小比例，计算所有元素应当是多少vw和vh</li>
<li>优点：可以动态针对某个元素进行计算，有针对性；当UI稿和实际屏幕尺寸不一致的时候，不会出现留白情况</li>
</ul>
</li>
<li>scale缩放：<ul>
<li>是什么：利用transform的scale属性整体按比例进行缩放</li>
<li>如何适配：<ul>
<li>按什么比例缩放：按照宽和高较小的缩放比例缩放，比如 实际屏幕宽/UI稿设计宽 = 2.5， 实际屏幕高/UI稿设计高 = 3，为了不让元素顶出去顶出去，最终按照2.5比例缩放</li>
<li>缩放基点问题：正常缩放是按照原来盒子中心进行缩放，如果直接缩放，也会有元素顶出去顶出去，应该要把基点放到屏幕正中心，如何做到？先利用定位top:50%，left:50%把盒子左上角放到中间，再把缩放基准按照这个左上角缩放，缩放完毕后，再利用translate:(-50%,-50%)把盒子拉回原处</li>
</ul>
</li>
<li>优点：代码少，简单；不用做单独做额外的适配</li>
</ul>
</li>
</ol>
<p>Ps：屏幕尺寸有====&gt;4：3、16：9（最常见）、21：9等</p>
</blockquote>
<h3 id="19-移动端屏幕适配的方案有哪些？"><a href="#19-移动端屏幕适配的方案有哪些？" class="headerlink" title="19.移动端屏幕适配的方案有哪些？"></a>19.移动端屏幕适配的方案有哪些？</h3><blockquote>
<ol>
<li><p>rem+动态计算html的fontSize（把屏幕宽或高设置为html的fontSize，而得出其他元素的多少rem）</p>
<p>也可以利用媒体查询计算fontSize，它可以根据屏幕宽高高度动态设置fontSize</p>
</li>
<li><p>vw和vh</p>
</li>
</ol>
</blockquote>
<h1 id="Js"><a href="#Js" class="headerlink" title="Js"></a>Js</h1><h3 id="1-Js由哪三部分组成？"><a href="#1-Js由哪三部分组成？" class="headerlink" title="1.Js由哪三部分组成？"></a>1.Js由哪三部分组成？</h3><blockquote>
<ol>
<li>ECMA script：它是js的核心，描述了js的语法，比如var、数组等</li>
<li>文档对象模型（DOM）：它把HTML页面看成标签构成的文档，提供API可以与页面交互</li>
<li>浏览器对象模型（BOM）：可提供与浏览器相关的API，与浏览器窗口交互</li>
</ol>
</blockquote>
<h3 id="2-Js有哪些内置对象"><a href="#2-Js有哪些内置对象" class="headerlink" title="2.Js有哪些内置对象?"></a>2.Js有哪些内置对象?</h3><blockquote>
<p>Boolean、Number、String、Array、Function、Object、Math、Date…</p>
<p>记忆技巧？数据类型-&gt;最常用的数据处理（Math、Date）</p>
<p>最常用的几个以及对应方法:</p>
<p>String：splice（切割）、split（根据特殊符号分成各个数组）、toString</p>
<p>Array：splice（最强数组方法，可增、删、改）、filter（过滤）、</p>
<p>Math：max()、min()、abs()、sqrt()</p>
<p>Date：getYear()…</p>
</blockquote>
<h3 id="3-操作数组的方法有哪些（常用）？"><a href="#3-操作数组的方法有哪些（常用）？" class="headerlink" title="3.操作数组的方法有哪些（常用）？"></a>3.操作数组的方法有哪些（常用）？</h3><blockquote>
<p>改变原数组：push()、pop()、unshift()、shift()、splice()、reverse()、sort()</p>
<p>记忆技巧？后（增删）、前（增删）、任意位置（增删）、简单排序（颠倒）、复杂排序</p>
<p>不改变原数组：filter（过滤器）、reduce（累加器）、map（元素任意处理）、join（连接成字符串）</p>
<p>记忆技巧？过滤获取部分-&gt;累加获取更多-&gt;reduce是map的特殊实现-&gt;数据处理完了连接成字符串来使用</p>
</blockquote>
<h3 id="4-对数据类型的检测方式有哪些？"><a href="#4-对数据类型的检测方式有哪些？" class="headerlink" title="4.对数据类型的检测方式有哪些？"></a>4.对数据类型的检测方式有哪些？</h3><blockquote>
<p>typeof ———————- 只能针对基本数据类型</p>
<p>intance of——————-只能针对引用类型</p>
<p>constructor—————–可以针对基本数据类型、引用类型，但是如果 原型被手动篡改 ，将判断错误（通过找原型名字）</p>
<p>Object.prototype.toString.call—万能方法，优先使用，可以针对基本数据类型、引用类型，基本不会出错，除非篡改 this指向或重写toString方法（很少会）（toString-&gt;结果是字符，call改变this获取后面的上下文环境的类型）</p>
<p>如何记忆？前两者都只针对一个，过于局限，后两者都是和原型有关系，且 通用，只是constructor 有可能圆形只想被篡改，推荐用Object.prototype.toString.call，这也是放在Object.prototype上的原因</p>
</blockquote>
<h3 id="5-请你说说闭包"><a href="#5-请你说说闭包" class="headerlink" title="5.请你说说闭包"></a>5.请你说说闭包</h3><blockquote>
<p>什么是闭包？是一个对象，key-value形式</p>
<p>形成条件？1）函数嵌套2）内部函数引用外部函数变量3）外部函数执行时</p>
<p>优缺点？优点：延长外部函数局部变量的生命周期；缺点：容易造成内存泄露</p>
<p>如何避免内存泄露？即使销毁。如何销毁？若外部函数执行后没有新的指向会自动成为垃圾对象被销毁，如果有新的指向，需要手动设置为null</p>
<p>使用场景？函数的防抖与节流、第三方库</p>
</blockquote>
<h3 id="6-请你说说内存泄露"><a href="#6-请你说说内存泄露" class="headerlink" title="6.请你说说内存泄露"></a>6.请你说说内存泄露</h3><blockquote>
<p>什么是内存泄露？指分配了内存，不使用时内存无法被释放的情况。</p>
<p>有哪些常见的内存泄露？</p>
<ul>
<li>不使用的全局变量（声明在函数外的都算，或者函数内部没有用var、let或const定义的，否则局部作用域一结束就会被回收）</li>
<li>定时器未清除</li>
<li>闭包的不正确使用（没有手动置空）</li>
</ul>
</blockquote>
<h3 id="7-事件委托是什么？"><a href="#7-事件委托是什么？" class="headerlink" title="7.事件委托是什么？"></a>7.事件委托是什么？</h3><blockquote>
<p>什么是 事件委托？就是利用 事件冒泡 将子元素的事件 绑定到父元素上</p>
<p>事件委托有什么好处？减少绑定的次数，提高性能</p>
<p>如何阻止事件委托？</p>
<ol>
<li>addEventListen(‘事件名’,function(event){ event.stopPropagation })</li>
<li>addEventListen(‘事件名’,回调,false) // false为事件冒泡，true为事件捕获</li>
</ol>
<p>阻止事件委托的使用场景？子元素可以有自己的事件，不需要触发父元素事件</p>
<p>什么是事件捕获？</p>
<p>事件冒泡是从内向外传播，而事件捕获相反，由外向内传播，它们都是事件传播的方式。以下是例子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs js">&lt;body&gt;<br>  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;outer&quot;</span>&gt;</span></span><br><span class="language-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;inner&quot;</span>&gt;</span></span><br><span class="language-xml">      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;btn&quot;</span>&gt;</span>Click me!<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span><br><span class="language-xml">    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span><br><span class="language-xml">  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span><br><br>  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="language-javascript"><span class="language-xml">   <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;btn&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Button clicked&#x27;</span>);</span></span><br><span class="language-javascript"><span class="language-xml">    &#125;);</span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">    <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;inner&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Inner div clicked (bubbling)&#x27;</span>);</span></span><br><span class="language-javascript"><span class="language-xml">    &#125;);</span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">    <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;outer&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Outer div clicked (bubbling)&#x27;</span>);</span></span><br><span class="language-javascript"><span class="language-xml">    &#125;);</span></span><br><span class="language-javascript"><span class="language-xml">    </span></span><br><span class="language-javascript"><span class="language-xml">    <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;outer&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Outer div clicked (capturing)&#x27;</span>);</span></span><br><span class="language-javascript"><span class="language-xml">    &#125;, <span class="hljs-literal">true</span>);</span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">    <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;inner&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;</span></span><br><span class="language-javascript"><span class="language-xml">      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Inner div clicked (capturing)&#x27;</span>);</span></span><br><span class="language-javascript"><span class="language-xml">    &#125;, <span class="hljs-literal">true</span>);</span></span><br><span class="language-javascript"><span class="language-xml"></span></span><br><span class="language-javascript"><span class="language-xml">  </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br>&lt;/body&gt;<br><br><span class="hljs-comment">// 结果</span><br><span class="hljs-title class_">Outer</span> div clicked (capturing)<br><span class="hljs-title class_">Inner</span> div clicked (capturing)<br><span class="hljs-title class_">Button</span> clicked<br><span class="hljs-title class_">Inner</span> div clicked (bubbling)<br><span class="hljs-title class_">Outer</span> div clicked (bubbling)<br></code></pre></td></tr></table></figure>

<p>事件触发的顺序遵循事件传播的三个阶段：默认传播方向为 事件捕获-&gt;目标-&gt;事件冒泡，和事件添加的顺序无关（即使先添加事件冒泡，还是先打印捕获）</p>
</blockquote>
<h3 id="8-基本数据类型和引用数据类型的区别是什么？"><a href="#8-基本数据类型和引用数据类型的区别是什么？" class="headerlink" title="8.基本数据类型和引用数据类型的区别是什么？"></a>8.基本数据类型和引用数据类型的区别是什么？</h3><blockquote>
<p>各有什么：</p>
<p>基本数据类型：String、Number、Boolean、null、undefined和Symbol</p>
<p>引用数据类型：Array、Function、Object</p>
<p>区别：存储位置的不同</p>
<p>基本数据类型的值和引用数据类型的地址 存放在栈内存中，而引用类型的值 存放在 堆内存中</p>
</blockquote>
<h3 id="9-说一下原型链"><a href="#9-说一下原型链" class="headerlink" title="9.说一下原型链"></a>9.说一下原型链</h3><blockquote>
<p>什么是原型？原型是用来为所有实例共享属性和方法的（一般是方法），通过prototype将方法放在原型上，不用全部存放在构造函数上了。</p>
<p>实例如何访问到原型的方法？通过隐式原型_proto_______，因为它指向构造函数的原型，因此可以访问到</p>
<p>原型链又是什么？在通过_proto_______往原型上找时可能没找到，此时又会向原型的原型去找，这样一直网上找的路径像一条链一样，故称原型链，下面附一张图：</p>
</blockquote>
<p><img src="/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/%E5%8E%9F%E5%9E%8B%E9%93%BE.png" srcset="/blog/img/loading.gif" lazyload alt="原型链"></p>
<h3 id="10-new关键字做了什么"><a href="#10-new关键字做了什么" class="headerlink" title="10.new关键字做了什么"></a>10.new关键字做了什么</h3><blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">newFun</span>(<span class="hljs-params">Fun,args</span>)&#123;<br> <span class="hljs-comment">// 1. 创建一个空对象</span><br>    <span class="hljs-keyword">let</span> obj = &#123;&#125;<br> <span class="hljs-comment">// 2. 将空对象的__proto___指向(为了获取构造函数的属性和方法)</span><br>    obj.<span class="hljs-property">__proto__</span> = <span class="hljs-title class_">Fun</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span><br> <span class="hljs-comment">// 3. 改变this指向，将Fun的this指向到obj身上，并把构造函数需要的参数传过来</span><br>    <span class="hljs-keyword">let</span> result = <span class="hljs-title class_">Fun</span>.<span class="hljs-title function_">apply</span>(obj,args)<br> <span class="hljs-comment">// 4.判断返回的结果，若为 引用类型，则返回该引用类型，否则返回空对象</span><br>    <span class="hljs-keyword">return</span> result instance <span class="hljs-keyword">of</span> <span class="hljs-string">&#x27;object&#x27;</span> ? result : obj<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">fn</span>(<span class="hljs-params">name</span>)&#123;<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span> = name <br>&#125;<br><br>fn.<span class="hljs-property">protptype</span>.<span class="hljs-property">say</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)&#123;<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;hi&#x27;</span>)<br>&#125;<br><br><span class="hljs-keyword">let</span> p1 = <span class="hljs-title function_">newFun</span>(fn,<span class="hljs-string">&#x27;张三&#x27;</span>) <span class="hljs-comment">// 这里就是用newFun来代替new关键字，就是new的内部实现</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(p1.<span class="hljs-property">name</span>) <span class="hljs-comment">// 张三</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(p1)<br></code></pre></td></tr></table></figure>
</blockquote>
<h3 id="11-Js是如何实现继承的？"><a href="#11-Js是如何实现继承的？" class="headerlink" title="11.Js是如何实现继承的？"></a>11.Js是如何实现继承的？</h3><blockquote>
<p>有四种方式：</p>
<ul>
<li><p>通过原型链的方式：将子类构造函数 原型 指向 父类的实例</p>
<p>子类.prototype = new 父类()</p>
<p>为什么是指向父类的实例而不是原型？</p>
<p>因为实例有原型链，指向实例就可以一直往上找想要的数据，而不是只在父类上找</p>
<p>缺点：无法传递参数给父亲进行动态父类数据初始化</p>
</li>
<li><p>借用父类构造函数：在子类构造函数内，调用父类构造函数并用call更改this指向，指向子类</p>
<p>function child(){</p>
<p>​      parent.call(this,’参数’)</p>
<p>}</p>
<p>为什么？调用父类构造函数是为了初始化，更改this指向是为了让子类使用</p>
<p>优缺点：改进了原型链的方式可以传参，但是无法共享父类数据，函数都是在构造函数中定义的</p>
</li>
<li><p>组合式：结合以上两种方式，即：</p>
<p>子类.prototype = new 父类()</p>
<p>function child(){</p>
<p>​      parent.call(this,’参数’)</p>
<p>}</p>
<p>优缺点：解决了上面的问题，但是又会调用两次父类构造函数</p>
</li>
<li><p>用ES6的class的entends和super关键字，必须先调用super，再初始化子类数据，在子类内部使用super.数据 获取继承过来的数据</p>
<p>class 子类 entends 父类{</p>
<p>​      constructor(参数){</p>
<p>​             super(参数)</p>
<p>​             初始化子类数据</p>
<p>​      }</p>
<p>}</p>
<p>本质：extends内部做了将子类和父类原型链关联的操作，而super类似call调用传参并改this，只是不需要调用两次父类构造函数</p>
<p>优缺点：更加完善，但是不是所有浏览器都支持ES6的class</p>
<p>为什么一定要必须先调用super，再初始化子类数据？</p>
<p>因为要确保父类先初始化完毕后，这样子类才能继承过来使用，否则父类的数据都没有值的。</p>
</li>
</ul>
</blockquote>
<h3 id="12-Js设计原理是什么？（问题太大，答案也不太好）"><a href="#12-Js设计原理是什么？（问题太大，答案也不太好）" class="headerlink" title="12.Js设计原理是什么？（问题太大，答案也不太好）"></a>12.Js设计原理是什么？（问题太大，答案也不太好）</h3><blockquote>
<p>包含以下几个部分：Js引擎、执行上下文、调用栈、事件循环、回调函数</p>
<p>Js引擎-&gt;编译成可执行代码，让代码去执行</p>
<p>执行上下文-&gt;可调用浏览器的API</p>
<p>调用栈-&gt;Js单线程就是不断地从调用栈取出代码来执行，比如函数压栈</p>
<p>事件循环和回调-&gt;处理异步问题，当调用栈为空时，再去除事件队列中的回调函数，不断循环</p>
<p>思路：先编译能识别代码-&gt;需要用到API-&gt;在调用栈执行代码-&gt;碰到异步问题 用事件循环和回调解决</p>
</blockquote>
<h3 id="13-Js中关于this指向问题"><a href="#13-Js中关于this指向问题" class="headerlink" title="13.Js中关于this指向问题"></a>13.Js中关于this指向问题</h3><blockquote>
<ol>
<li><p>一般来说，this永远指向最后调用它的对象（可能层级太多），比如单独函数调用fn()，其实也是window.fn()；如果没有调用者，那就只想全局作用域下的this（浏览器window，Nodejs为global）</p>
</li>
<li><p>new fn()，这里面涉及到this指向的改变，指向了实例</p>
</li>
<li><p>说到this指向更改，call、apply、bind都可以改变，区别后面会说，但是不能是箭头函数，因为箭头函数本身就没有this的概念，更不用说更改</p>
</li>
<li><p>箭头函数是没有this的，直接忽略箭头函数那段代码，然后看当前this在哪，默认来看this指向  箭头函数的外层函数的this（父作用域下的this），没有外层函数就window，其实就是当箭头函数不存在</p>
</li>
<li><p>说到箭头函数这样的特殊函数，还有个特殊函数：匿名函数，永远指向window，因为默认没有调用者</p>
<hr>
<p>以上是ES5的this指向，下面说说ES6以及以后：</p>
<p>由于现在前端js越往后越趋向于后端的面向对象的理念，需要像后端这种类中的this自动绑定到</p>
</li>
</ol>
<p>思路：一般性this指向-&gt;特殊例子new，因为new没有调用者，涉及this更改-&gt;如何更改?call、apply、bind，但是不是箭头函数-&gt;那就说箭头函数-&gt;和箭头函数一样的特殊函数</p>
</blockquote>
<h3 id="14-script标签中defer和async的区别是什么？"><a href="#14-script标签中defer和async的区别是什么？" class="headerlink" title="14.script标签中defer和async的区别是什么？"></a>14.script标签中defer和async的区别是什么？</h3><blockquote>
<p>两者都不会阻塞页面的渲染，都属异步处理方案，不同的是：</p>
<p>defer：会等待页面解析、渲染完毕才会执行，优点是 保证了js执行顺序</p>
<p>async：不会等待页面处理完毕，和页面处理 并行处理，优点是 执行效率会更高</p>
<p>各自使用场景：需要保证执行脚本的顺序 -&gt; defer，否则 -&gt;async</p>
<p>如何记忆？看翻译：defer-&gt;推迟，async-&gt;异步（一般是指并行）</p>
<p>下面附代码例子：注意修饰的是script脚本！</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Defer Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;script1.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;script2.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>    <br></code></pre></td></tr></table></figure>

<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Async Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">async</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;script1.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">async</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;script2.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br></code></pre></td></tr></table></figure>
</blockquote>
<h3 id="15-定时器最小执行时间各为多少？"><a href="#15-定时器最小执行时间各为多少？" class="headerlink" title="15.定时器最小执行时间各为多少？"></a>15.定时器最小执行时间各为多少？</h3><blockquote>
<p>setTimeout最小执行时间为4ms</p>
<p>setInterval最小执行时间为10ms</p>
<p>以上是HTML5规定的，不同浏览器可能有误差，但是至少得要有4ms</p>
<p>为什么要这么规定？</p>
<p>为了浏览器的性能，如果最小执行时间是0ms，且有多个这样的定时器的话，那么在短时间内就会执行大量的任务，这对浏览器的性能造成负面影响。</p>
</blockquote>
<h3 id="16-call、apply和bind有什么区别"><a href="#16-call、apply和bind有什么区别" class="headerlink" title="16.call、apply和bind有什么区别"></a>16.call、apply和bind有什么区别</h3><blockquote>
<p>都是用于改变this指向和执行函数的，区别如下：</p>
<ul>
<li><p>call、apply和bind的区别在于：call、apply会立刻执行函数，而bind不会，bind第一次执行会返回一个改变了this指向的新的函数，再在第二次执行才会执行函数（使用场景例子：添加点击事件时用bind绑定，用户点击时执行）</p>
</li>
<li><p>call和apply的区别在于：前者参数传递的是对象，后者是数组。又call比apply性能要好，且数组可以用扩展运算符放入对象中，因此call用的比较多</p>
</li>
</ul>
</blockquote>
<h3 id="17-在使用递归的时候有没有遇到什么问题？"><a href="#17-在使用递归的时候有没有遇到什么问题？" class="headerlink" title="17.在使用递归的时候有没有遇到什么问题？"></a>17.在使用递归的时候有没有遇到什么问题？</h3><blockquote>
<p>递归是指：在函数中自己调用自己</p>
<p>问题：一定要有return的出口，否则无限制地调用函数，会导致调用栈溢出</p>
</blockquote>
<h3 id="18-如何实现一个深拷贝？"><a href="#18-如何实现一个深拷贝？" class="headerlink" title="18.如何实现一个深拷贝？"></a>18.如何实现一个深拷贝？</h3><blockquote>
<p>有三种方式实现深拷贝：</p>
<ol>
<li><p>用扩展运算符，缺点：只能实现第一层的深拷贝，多层就是浅拷贝了，因为涉及到多层展开仍然是个对象，地址还是一样的</p>
</li>
<li><p>json.parse(json.stringfy())，先转换为字符串，再转换为json，缺点：无法针对函数转换，因为函数转换为字符串就是个函数名，无法把内部逻辑转换</p>
</li>
<li><p>手写一个深拷贝，以解决上面的问题：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">deepClone</span>(<span class="hljs-params">source</span>)&#123;<br>    <span class="hljs-keyword">let</span> newObj = source.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">constructor</span> == <span class="hljs-string">&#x27;object&#x27;</span> ? &#123;&#125; : []<br>    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> keys <span class="hljs-keyword">in</span> source)&#123;<br>        <span class="hljs-comment">// 要判断下Source上的属性是否是继承自原型的,因为只要拷贝自身属性,而不要原型上的</span><br>        <span class="hljs-keyword">if</span>(source.<span class="hljs-title function_">hasOwnproperty</span>(keys))&#123;<br>            <span class="hljs-keyword">let</span> value = newObj[keys]<br>            <span class="hljs-keyword">if</span>(value &amp;&amp; value.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">constructor</span> == <span class="hljs-string">&#x27;object&#x27;</span>)&#123;<br>                newObj[keys] = <span class="hljs-title function_">deepClone</span>(value)<br>            &#125;<span class="hljs-keyword">else</span>&#123;<br>                newObj[keys] = value<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
</ol>
</blockquote>
<h3 id="19-说一下Js事件循环"><a href="#19-说一下Js事件循环" class="headerlink" title="19.说一下Js事件循环"></a>19.说一下Js事件循环</h3><blockquote>
<p>是什么：Js事件循环是Js处理异步操作的机制，下面说说这个机制的过程是什么</p>
<p>过程：Js主线程碰到同步任务时，会放在执行栈（专门用来执行任务的数据结构，先进来的后执行，执行完毕移除栈顶任务）中来执行，一旦碰到异步任务，就会添加放在任务队列中，等到执行栈中任务执行完毕，就会从任务队列中去除异步任务到执行栈中执行，就这样一旦执行栈为空便从任务队列去取异步任务来执行，循环往复的过程就是事件循环。</p>
<p>什么是异步？</p>
<p>就是 不会因为要执行当前的任务 而阻塞下面任务的执行。</p>
<p>异步任务分为哪些？</p>
<p>宏任务：定时器、requestAnimationFrame（下一次动画帧执行函数）、网络请求、setImmediate（任务添加到当前宏任务的末尾—后端特有）、IO操作（文件读写—后端特有）</p>
<p>如何记忆？在定时器、动画中进行网络请求，让后端在setImmediate处理IO操作</p>
<p>微任务：promise(回调).then()、process.nextTick（把任务添加到当前微任务的末尾–后端特有）</p>
<p>宏任务和微任务的处理顺序是什么？</p>
<p>浏览器端：第一个宏任务（主线程）-&gt;微任务-&gt;宏任务（微任务优先级比宏任务优先级高，因为微任务一般处理紧急任务，而宏任务一般处理耗时的操作），每个宏任务之间会进行一次页面渲染</p>
<p>Nodejs：在6个阶段，每一个阶段完成后都会去执行一批微任务（有的话）</p>
<p>你这里面说有些是后端特有的是什么意思？</p>
<p>因为在前端浏览器和后端Nodejs都有一个事件循环机制，功能是类似的，都有宏任务和微任务，只是 浏览器端的事件循环机制是处理前端异步操作的，后端Nodejs的事件循环机制是处理后端异步操作的（注意：Nodejs本身是单线程的，但是可能会碰到一些事件循环机制处理不了的一些需要大量计算的异步任务，这时候可能会借助多线程的模块处理）。</p>
<p>setImmediate和setTimeout（时间延迟设置为0）的区别是什么？</p>
<p>setImmediate是会让任务添加到当前事件循环的末尾来执行。两者区别在于：setTimeout是在事件循环的第一个阶段timer，处理的，而setImmediate则在末尾check阶段，如果setTimeout延迟为0ms的话，setTimeout会比setImmediate先执行。</p>
<p>如何记忆？事件循环机制阶段的不同</p>
<p>为什么要用到任务队列来处理异步操作？</p>
<p>因为Js是单线程的，没有多线程来处理异步操作，只能先放在队列中稍后执行。</p>
<p>​    为什么Js是单线程的？</p>
<p>​    因为多线程会带来很多同步问题，比如死锁（举例子就说路面上的堵车，都在等待其他人腾出空间，但是也腾不出空间而造成的僵局）。</p>
<p><u>思路：</u>是什么-&gt;过程-&gt;涉及到异步-&gt;异步任务（有后端特有的任务以及区别）-&gt;任务队列</p>
</blockquote>
<h3 id="20-什么是防抖和节流"><a href="#20-什么是防抖和节流" class="headerlink" title="20.什么是防抖和节流"></a>20.什么是防抖和节流</h3><blockquote>
<p>这两者都是避免频繁触发事件来提高性能的。</p>
<p>防抖：防止不必要的操作（抖动），来节省资源，只在必要的时刻（<u>集中触发的最后一次</u>）触发</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">debounce</span>(<span class="hljs-params">fn,delay</span>)&#123;<br> <span class="hljs-keyword">var</span> timer;<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)&#123;<br>     <span class="hljs-comment">// 只要在delay内多次触发，前一次即将到达delay的时间要执行fn时，就会被下一次给clear掉了</span><br>     <span class="hljs-comment">// 只要时间到达了dalay，就被看做是确定要触发了，比如用户输入停下来了，用户停下来之前都不会触发</span><br>     <span class="hljs-built_in">clearTimeout</span>(timer)<br>     timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)&#123;<br>     <span class="hljs-title function_">fn</span>()<br>    &#125;,delay)<br> &#125;<br>&#125;<br><br><span class="hljs-comment">// 为了好的效果，delay不能设置太小-&gt;触发仍会频繁，也不能设置太大-&gt;用户输入完了等半天，要预估好 用户操作大致要花多长时间</span><br></code></pre></td></tr></table></figure>

<p>使用场景：用户输入框、搜索框</p>
<p>节流：直接通过<u>降低</u><u>频率</u>来避免频繁触发事件</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">throttle</span>(<span class="hljs-params">fn,delay</span>)&#123;<br> <span class="hljs-keyword">var</span> lastTime;<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)&#123;<br>     <span class="hljs-keyword">var</span> nowTime = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>()<br>     <span class="hljs-comment">// 规定在delay内执行一次，比防抖容易理解</span><br>     <span class="hljs-keyword">if</span>(now - lastTime &gt; delay)&#123;<br>         <span class="hljs-title function_">fn</span>()<br>         lastTime = nowTime<br>     &#125;<br> &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>使用场景：滚动条 设置 图片懒加载 的事件（也可以通过判断高度来实现–用得更多）</p>
<p>用 花钱 来比喻：防抖是 只去买有用的生活必需品，节流是 针对奢侈品 少买。</p>
</blockquote>
<h3 id="21-什么是json？"><a href="#21-什么是json？" class="headerlink" title="21.什么是json？"></a>21.什么是json？</h3><blockquote>
<p>json是一个字符串的数据格式，用大括号包裹，内容是键值对的形式，并用逗号分隔。</p>
<p>它经常用在前后端数据交互时传输数据、配置文件比如package.json</p>
<p>数据交互时如果不是json数据，可以使用序列化（JSON.stringfy）和反序列化（JSON.parse）进行转换，当然这两个函数还可以用来实现深拷贝。</p>
</blockquote>
<h3 id="22-函数内部有执行上下文环境吗？"><a href="#22-函数内部有执行上下文环境吗？" class="headerlink" title="22.函数内部有执行上下文环境吗？"></a>22.函数内部有执行上下文环境吗？</h3><blockquote>
<p>有，但是它是虚拟地存在于栈中，在函数内是无法打印出来的，如果在函数打印this，它会往外面找，也就是全局作用域下的this-&gt;window。</p>
<p>但是一旦在函数内添加了属性和方法，内部会添加到的栈中，想要访问也可以通过this拿出来。</p>
</blockquote>
<h1 id="ES"><a href="#ES" class="headerlink" title="ES"></a>ES</h1><h3 id="1-ES5和ES6有什么区别？"><a href="#1-ES5和ES6有什么区别？" class="headerlink" title="1.ES5和ES6有什么区别？"></a>1.ES5和ES6有什么区别？</h3><blockquote>
<p>ES是指ECMAScript，它是Js的组成部分，另外两个部分是BOM、DOM</p>
<p>ES5-&gt;ECMAScript5，即在2009年ECMAScript第五次修订，也称为ECMAScript2009</p>
<p>ES6-&gt;ECMAScript6，即在2015年ECMAScript第六次修订，也称为ECMAScript2015</p>
</blockquote>
<h3 id="2-ES6有哪些新特性？"><a href="#2-ES6有哪些新特性？" class="headerlink" title="2.ES6有哪些新特性？"></a>2.ES6有哪些新特性？</h3><blockquote>
<p><strong>变量的声明</strong></p>
<p>新增了let和const，有以下特性：</p>
<ul>
<li>没有变量提升</li>
<li>会有暂时性死区的问题（不能先使用再声明）</li>
<li>不能重复定义</li>
<li>有块级作用域</li>
</ul>
<p><u>思路</u>：针对var的特性的不足来，从var的声明（变量提升-&gt;数量一多-&gt;重复声明；范围一广-&gt;也会覆盖-&gt;全局作用域）</p>
<p><strong>基本数据类型</strong></p>
<p>新增了一个Symbol类型，特性如下：</p>
<ul>
<li><p>独一无二的属性</p>
</li>
<li><p>不想被发现的特性</p>
</li>
</ul>
<p>针对第二点，可以是的数据私有化：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> _age = <span class="hljs-title class_">Symbol</span>(<span class="hljs-string">&#x27;age&#x27;</span>);<br><span class="hljs-keyword">const</span> _getAge = <span class="hljs-title class_">Symbol</span>(<span class="hljs-string">&#x27;getAge&#x27;</span>);<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Person</span> &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">age</span>) &#123;<br>    <span class="hljs-variable language_">this</span>[_age] = age;<br>  &#125;<br><br>  [_getAge]() &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>[_age];<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> john = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">30</span>);<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(john.<span class="hljs-property">_age</span>); <span class="hljs-comment">// undefined，私有属性无法直接访问</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(john.<span class="hljs-title function_">_getAge</span>()); <span class="hljs-comment">// TypeError，私有方法无法直接调用</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(john[_age]); <span class="hljs-comment">// undefined，通过 Symbol 访问私有属性也无法获取到</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(john[_getAge]()); <span class="hljs-comment">// 输出: 30，只有通过对应 Symbol 的方法才能获取私有属性值</span><br></code></pre></td></tr></table></figure>

<p><u>思路</u>：有不想被发现的特性-&gt;私有化</p>
<p><strong>数组</strong></p>
<p>数组在定义时：</p>
<p>可以用到 解构赋值和扩展运算，对数据 进行 拆分赋值 和合并</p>
<p>数组在处理数据时：</p>
<p>可以用到 for循环遍历，有 of（item-&gt;单个元素） 和 in（item-&gt;下标） 之分</p>
<p>处理数据有更便捷、自带的函数：</p>
<p>set-&gt;自动去重；</p>
<p>map-&gt;键可任意，可配合forEach遍历（和数组关联起来了）</p>
<p><u>思路</u>：数组定义时，元素的拆分-&gt;解构赋值与合并-&gt;扩展运算符；数组定义后要遍历处理数据-&gt;for的in和of-&gt;自带函数更方便地处理-&gt;set和map的好处</p>
<p><strong>导入外部模块</strong></p>
<p>新增class-&gt;继承（见上面继承方式）</p>
<p>新增暴露和导入，将写的class导入，首先有几种暴露方式：</p>
<ul>
<li>分别暴露（每个数据写个export）</li>
<li>统一暴露（最后用个对象{}包裹）</li>
<li>默认暴露（边暴露便定义并写在一个对象中）</li>
</ul>
<p>导入方式：</p>
<ul>
<li>导入所有（import * as 别名）</li>
<li>解构赋值方式（import {挑选}）</li>
<li>默认导入（import 别名）—–只针对默认暴露</li>
</ul>
<p><u>思路</u>：class一般专门定义在一个js文件中，再倒入使用</p>
<p><strong>函数</strong></p>
<p>函数的形参，可以用到解构赋值，目的是更快速地拿到对象中的属性</p>
<p>​                           会用到默认参数，此参数用于一般不会更改的情况</p>
<p>​                           会用到扩展运算符的rest参数，通常叫…args</p>
<p>新增箭头函数，先说下它的参数：</p>
<ul>
<li>没有普通函数的arguments，取而代之的是args，功能一样，和简写的箭头函数更搭配</li>
</ul>
<p>再说下 关于this指向带来的特性：</p>
<ul>
<li>它完全没有this的概念</li>
<li>没有this就不能当作构造函数new一个实例，因为new涉及到this的更改，都没有this，也无法更改</li>
<li>不能作为构造函数，就没有原型，因为原型是构造函数才有的</li>
<li>不能用call、apply、bind更改this指向</li>
<li>this指向外层最近的函数的指向（父作用域下的this），就是直接忽视箭头函数</li>
</ul>
<p><u>思路</u>：普通函数：形参的处理-&gt;解构赋值-&gt;默认参数-&gt;扩展运算符；</p>
<p>​            特殊函数（函数的简写）：由外到内</p>
<p>​                  外：形参</p>
<p>​                  内：没有this的概念-&gt;没有new的概念（要改变this）-&gt;没有作为构造函数-&gt;没有原型；那怎么改？就算 call、apply、bind也无法改变；那this怎么看？指向它最近外层函数的this（就是忽视整个箭头函数即可）</u></p>
<p><strong>对象</strong></p>
<p>对象的属性和函数新增简写形式：</p>
<ul>
<li>已经有属性和函数，直接写属性名和函数名即可，不需要写冒号:</li>
<li>对于函数的声明可以直接写成函数形式：fn(){}</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> a = &#123;<br>    属性,<br>    函数<span class="hljs-number">1</span>,<br>    <span class="hljs-title function_">fn</span>(<span class="hljs-params"></span>)&#123;...&#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><u>思路</u>：不需要冒号来简写</p>
<p><strong>发送异步请求</strong></p>
<p>新增Promise</p>
<p>目的：解决回调地狱的问题</p>
<p>什么是回调地狱？</p>
<p>在promise之前，如果碰到有多个异步任务有依赖关系，这时候只能通过嵌套的回调函数来实现，这就形成了 回调地狱，可读性差（要看很多行）-&gt;可维护性差（难以查找来修改）</p>
<p>有什么：自身包括 resolve、reject、all、allSettled、race方法</p>
<p>​               all、allSettled的区别？都是传入数组，存放着所有promise，返回一个新的promise结果（相当于then），前者是 有一个失败，就返回失败，后者 都会返回成功的结果，里面有所有promise的结果。</p>
<p>​              race方法-&gt;哪个promise先执行完毕就返回哪个promise的结果，race-&gt;找竞速为第一名的</p>
<p>​                             使用场景：超时机制-&gt;网络请求超过一定时间就弹出错误信息，就是看成功还是失败看哪个先来。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">fetchWithTimeout</span>(<span class="hljs-params">url, timeout</span>) &#123;<br>  <span class="hljs-comment">// 创建一个待定的 Promise 对象，该对象在超时后返回一个默认值或错误信息</span><br>  <span class="hljs-keyword">let</span> timeoutPromise = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-title function_">reject</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&#x27;Request timed out&#x27;</span>));<br>    &#125;, timeout);<br>  &#125;);<br><br>  <span class="hljs-comment">// 发起请求的 Promise 对象</span><br>  <span class="hljs-keyword">let</span> fetchPromise = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>    <span class="hljs-title function_">fetch</span>(url)<br>      .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.<span class="hljs-title function_">json</span>())<br>      .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> &#123;<br>        <span class="hljs-title function_">resolve</span>(data);<br>      &#125;)<br>      .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> &#123;<br>        <span class="hljs-title function_">reject</span>(error);<br>      &#125;);<br>  &#125;);<br><br>  <span class="hljs-comment">// 使用 Promise.race() 等待任意一个 Promise 对象完成</span><br>  <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">race</span>([fetchPromise, timeoutPromise]);<br>&#125;<br><br><span class="hljs-comment">// 使用示例</span><br><span class="hljs-title function_">fetchWithTimeout</span>(<span class="hljs-string">&#x27;https://api.example.com/data&#x27;</span>, <span class="hljs-number">5000</span>)<br>  .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Request succeeded:&#x27;</span>, data);<br>  &#125;)<br>  .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Request failed:&#x27;</span>, error.<span class="hljs-property">message</span>);<br>  &#125;);<br><br></code></pre></td></tr></table></figure>

<p>​               原型上包括 then、catch方法</p>
<p>如何使用的？</p>
<p>将异步操作用Promsie包裹，成功用resolve把值传递出，此时状态由pending-&gt;resolved，否则用reject传递失败结果，状态由pending-&gt;rejected，最后用then、catch的回调函数获取结果</p>
<p>为什么值传递出去，then方法里就可以获取到？原理是什么？</p>
<p>内部实现用到了发布订阅模式。在Promise的内部会针对状态成功和状态失败的回调函数的数组，在then方法里，会把传进来的回调函数添加到回调数组中（这时就是订阅了，为了以后获得消息），然后再在Promise构造函数内部的Resolve和Reject函数里会逐一执行刚刚的回调函数数组，并且会把resolve和reject传过来的value参数传入回调函数中（这时就是发布了），最终在链式调用then时就可以获得依次执行回调的结果了。</p>
<p>——&gt;链式是如何实现的？（比较难，说个大致了解即可）</p>
<p>我说个大致了解啊：刚刚说了用于链式调用的各个回调被存放在回调数组中，怎么实现在调用then的时候按顺序执行呢？就是在每次执行回调时先判断下返回值是否为Promise，是的话用then继续调用，不是的话就resolve出去。</p>
<p><u>思路</u>：目的-&gt;有什么（先介绍有什么才能更好地说明如何使用）-&gt;如何使用-&gt;原理</p>
<p><strong>数据回来展示</strong></p>
<p>新增模板字符串，可以把变量 插入 字符串中</p>
<p><u>整体思路</u>：定义数据（基本类型、数组、函数、对象）-&gt;引入外部数据-&gt;处理数据-&gt;发送异步请求-&gt;返回数据-&gt;展示</p>
</blockquote>
<h3 id="3-Promise和Async、Await的区别是什么？"><a href="#3-Promise和Async、Await的区别是什么？" class="headerlink" title="3.Promise和Async、Await的区别是什么？"></a>3.Promise和Async、Await的区别是什么？</h3><blockquote>
<p>共同点：都是处理异步操作的解决方案。</p>
<p>使用区别：Promise-&gt;见上面；而Async修饰一个返回Promise对象的异步函数，Await则可以等待一个Promise对象的解决</p>
<p>后者优势：最大的优势就是 Async、Await可以像编写同步代码一样处理异步操作，更加易读、易于理解。</p>
</blockquote>
<h1 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h1><h3 id="1-Ajax是什么？如何实现的？"><a href="#1-Ajax是什么？如何实现的？" class="headerlink" title="1.Ajax是什么？如何实现的？"></a>1.Ajax是什么？如何实现的？</h3><blockquote>
<p>定义：它是一个实现交互式网页的技术，可以在保证页面不刷新的情况下，和服务器交互数据并更新部分内容</p>
<p><u>思路</u>：什么技术-&gt;功能（页面不刷新-&gt;用户体验好，更新部分内容-&gt;场景用得多）</p>
<p>实现过程（<u>思路</u>）：利用xmlHttpRequest对象，先服务器发送请求并返回数据，最终用Js更新页面部分内容，具体如下：</p>
<ol>
<li>创建一个xmlHttpRequest对象</li>
<li>利用该对象的open函数打开连接</li>
<li>利用该对象的send函数向服务器发送数据</li>
<li>利用onreadystate change事件监听通信状态</li>
<li>服务器返回数据</li>
<li>利用Js将数据更新到页面上</li>
</ol>
<p>onload事件和onreadystatechange事件有什么区别？</p>
<p>onload事件是在请求成功时触发，而onreadystatechange事件是在请求的整个过程的状态（更全：请求-&gt;连接-&gt;发送数据-&gt;返回数据）变化了都会触发，简而言之，处理成功的结果用onload，处理请求的请求过程中间状态用onreadystatechange。</p>
<p><u>思路</u>：顾名思义，load-&gt;加载完成，statechange-&gt;中间状态改变</p>
</blockquote>
<h3 id="2-get和post有什么区别？"><a href="#2-get和post有什么区别？" class="headerlink" title="2.get和post有什么区别？"></a>2.get和post有什么区别？</h3><blockquote>
<p>使用场景：get一般用于获取数据，post一般用于提交数据</p>
<p>参数位置：get放在URL中，post则放在body中</p>
<p>参数编码：get只能用类似于键值对的方式进行编码，而post支持很多种（json格式、纯文本格式、二进制-&gt;上传文件）</p>
<p>安全性：get把参数放在URL中，安全性比post要差</p>
<p>参数限制：由于URL长度有限制，于是get的参数是由长度限制的，而post没有</p>
<p>缓存：get由于一般只是获取服务区数据，因此设有缓存机制来提高性能，而post提交数据会改变服务器数据，因此没有缓存机制。</p>
<p><u>思路</u>：使用场景-&gt;使用过程（传递的参数-&gt;参数如何编码的-&gt;安全性(1)-&gt;参数的限制(2)-&gt;数据回来 是否可缓存）</p>
</blockquote>
<h3 id="3-HTTP协议规定的请求头和响应头各包含什么？"><a href="#3-HTTP协议规定的请求头和响应头各包含什么？" class="headerlink" title="3.HTTP协议规定的请求头和响应头各包含什么？"></a>3.HTTP协议规定的请求头和响应头各包含什么？</h3><blockquote>
<p>请求头：</p>
<ul>
<li>User-Agent：通常指浏览器标识，指浏览器代理用户的标识（我是谁）</li>
<li>Referer：表示浏览器URL，用于防盗（我从哪来，避免其他网站获取网页资源）</li>
<li>Host（发给谁）</li>
<li>Authortization：权限信息（出示身份才能获取）</li>
<li>Content-Type（准备发送什么类型的数据）</li>
<li>Cookie（用什么存储-自动携带，存储个性化配置等）</li>
<li>Accept（希望返回什么类型的数据）</li>
<li>if-mdified-since或者if-none-match（得到数据后是否请求缓存—协商缓存）</li>
</ul>
<p>响应头：</p>
<ul>
<li>Server（我是谁）</li>
<li>Location（发给谁）</li>
<li>Content-Type（准备发送什么类型的数据）</li>
<li>Set-Cookie（用什么存储）</li>
<li>Expires/Cache-Control-强缓存、Lasted-Modified/Etag-协商缓存（决定是否让浏览器缓存下来）</li>
<li>Access-Control-Allow-Origin：设置允许的跨域来源（如果你是跨域的，下一次访问允许谁能访问）</li>
</ul>
</blockquote>
<h3 id="4-说一下同源策略"><a href="#4-说一下同源策略" class="headerlink" title="4.说一下同源策略"></a>4.说一下同源策略</h3><blockquote>
<p><a target="_blank" rel="noopener" href="http://www.example.com:3000/vue.js">http://www.example.com:3000/vue.js</a></p>
<p>协议   域名   子域名              端口号   资源</p>
<p>同源策略是指 协议、域名、端口号这三者有一个不同，就会收到跨域限制，即无法请求数据，这么做的目的以及好处是 避免来自不同域之间通信时受到攻击，从而降低风险。</p>
<p>如何实现跨域？有以下四种方式。</p>
<ol>
<li><p>jsonp</p>
<p>定义：通过利用script标签不受跨域限制的特点（因为外部脚本的来源经常来自不同的域，如果限制，无法满足动态加载的需求），利用script发送get请求实现跨域。</p>
<p>缺点：但是不推荐使用，因为不能完全保证script内的代码没有恶意代码来窃取隐私信息从而形成XSS攻击。</p>
<p>配合者：需要客户端和服务器端同时配置才可以，服务端配置和cors一样。</p>
</li>
<li><p>cors</p>
<p>只需要在服务器端配置响应头的选项，比如Access-Control-Allow-Origin等，因为浏览器是默认支持cors的。</p>
<p>有如上的优点，推荐使用这种方式的</p>
</li>
<li><p>websocket</p>
<p>它是一种可以实现实时的全双工通信的方式，一旦连接建立，客户端和服务器端可以相互发送请求。由于双向通信通产需要在不同域之间进行，因此websocket没有跨域限制，可以利用websocket进行跨域请求。</p>
</li>
<li><p>反向代理</p>
<p>它是利用在客户端和目标服务器之间添加一个中转站用来代理服务器（代理客户端是正向代理，代理服务器就是反向代理）操作来实现的跨域。</p>
<p>如何实现的？</p>
<p>这个中转站通常和客户端是同源的，这保证了客户端和中转站不受跨域限制，而中转站和目标服务器 都是服务器，服务器之间通信不会涉及到像浏览器端有用户的隐私数据，因为服务器之间是没有类似跨域限制这种限制的，这保证了中转站和目标服务器可以通畅无阻，最终实现了客户端与目标服务器的跨域请求。</p>
</li>
</ol>
</blockquote>
<h3 id="5-什么是无感登录？"><a href="#5-什么是无感登录？" class="headerlink" title="5.什么是无感登录？"></a>5.什么是无感登录？</h3><blockquote>
<p>就是 避免频繁登录给 用户带来不好的体验，最典型的就是 token过期后，会自动获取新的token并重新跳转</p>
<p>如何实现？</p>
<ul>
<li>在前端响应器中，判断状态码如果是401-&gt;token过期，则重新调用获取token的接口，并且重新请求页面地址 ———– 推荐第一种</li>
<li>也可以 用定时器，定时器 定时刷新token，但是时间不好把控，时间太短浪费网络资源</li>
</ul>
</blockquote>
<h3 id="6-如何实现大文件上传？"><a href="#6-如何实现大文件上传？" class="headerlink" title="6.如何实现大文件上传？"></a>6.如何实现大文件上传？</h3><blockquote>
<p>大文件上传通常会碰到 上传速度慢、网络中断 的问题</p>
<p>针对上面的问题，通常有以下两个策略：</p>
<ul>
<li><p>分片上传：</p>
<ol>
<li><p>将大文件按照一定规则分片（利用fs.splice），可以固定大小平分，这样的话最后一片大小会比较小，需要单独处理（如下）（有了分片个数和已发送分片就可以制作进度条了）；也可以根据网络情况分片（如下使用浏览器自带的API），网络情况差就分得小一些，根据网络负载能力来。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">splitFile</span>(<span class="hljs-params">file, chunkSize</span>) &#123;<br>  <span class="hljs-keyword">const</span> fileSize = file.<span class="hljs-property">size</span>;<br>  <span class="hljs-keyword">const</span> totalChunks = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">ceil</span>(fileSize / chunkSize);<span class="hljs-comment">// 个数</span><br>  <span class="hljs-keyword">const</span> chunks = [];<br>  <br>  <span class="hljs-keyword">let</span> start = <span class="hljs-number">0</span>;<br>  <span class="hljs-keyword">let</span> end = chunkSize;<br>    <br>  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; totalChunks; i++) &#123;<br>  <span class="hljs-keyword">let</span> chunk;<br>  <br>  <span class="hljs-comment">// 最后一块单独处理</span><br>  <span class="hljs-keyword">if</span> (i === totalChunks - <span class="hljs-number">1</span>) &#123;<br>    chunk = file.<span class="hljs-title function_">slice</span>(start);<br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    chunk = file.<span class="hljs-title function_">slice</span>(start, end);<br>  &#125;<br>  <br>  chunks.<span class="hljs-title function_">push</span>(chunk);<br>  <br>  start = end;<br>  end = start + chunkSize;<br>&#125;<br>    <br>  <span class="hljs-keyword">return</span> chunks;<br>&#125;<br><br><span class="hljs-comment">// 示例用法</span><br><span class="hljs-keyword">const</span> file = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;fileInput&#x27;</span>).<span class="hljs-property">files</span>[<span class="hljs-number">0</span>];<br><span class="hljs-keyword">const</span> chunkSize = <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>; <span class="hljs-comment">// 1MB</span><br><span class="hljs-keyword">const</span> chunks = <span class="hljs-title function_">splitFile</span>(file, chunkSize);<br></code></pre></td></tr></table></figure>

<blockquote>
<p>检测网络情况：</p>
<p>在前端（客户端）可以通过浏览器提供的API如<code>navigator.connection</code>或第三方库来获取当前网络类型、效果和速度等信息。例如，<code>navigator.connection.type</code>可以返回网络类型（如wifi、4G、3G等），<code>navigator.connection.effectiveType</code>可以返回网络效果（如”slow-2g”、”4g”等），<code>navigator.connection.downlink</code>可以返回当前网络的下载速度。</p>
</blockquote>
</li>
<li><p>将分片一个个上传，同时，记录每个分片的序列号，用于给服务端检查是否有遗漏的，保证完整性</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 客户端</span><br><span class="hljs-keyword">let</span> sequenceNumber = <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">processFileBlock</span>(<span class="hljs-params">fileBlock</span>) &#123;<br>  <span class="hljs-comment">// 递增计数器</span><br>  sequenceNumber++;<br><br>  <span class="hljs-comment">// 将序列号与文件块一同发送给服务端</span><br>  <span class="hljs-title function_">sendFileBlock</span>(fileBlock, sequenceNumber);<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">sendFileBlock</span>(<span class="hljs-params">fileBlock, sequenceNumber</span>) &#123;<br>  <span class="hljs-comment">// 发送文件块和序列号给服务端</span><br>  <span class="hljs-comment">// ...</span><br>&#125;<br><br><span class="hljs-comment">// 服务端</span><br><span class="hljs-keyword">let</span> expectedSequenceNumber = <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">receiveFileBlock</span>(<span class="hljs-params">fileBlock, sequenceNumber</span>) &#123;<br>  <span class="hljs-comment">// 根据接收到的序列号与期望的序列号进行比对</span><br>  <span class="hljs-keyword">if</span> (sequenceNumber === expectedSequenceNumber + <span class="hljs-number">1</span>) &#123;<br>    <span class="hljs-comment">// 序列号正确，处理文件块</span><br>    <span class="hljs-title function_">processFileBlock</span>(fileBlock);<br><br>    <span class="hljs-comment">// 更新期望的序列号</span><br>    expectedSequenceNumber++;<br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    <span class="hljs-comment">// 序列号不匹配，要求重传缺失的文件块</span><br>    <span class="hljs-title function_">requestMissingFileBlock</span>(expectedSequenceNumber + <span class="hljs-number">1</span>);<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
<li><p>服务端在检查完所有的分片之后，利用fs模块的可读流和可写流进行合并</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;fs&#x27;</span>);<br><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;path&#x27;</span>);<br><br><span class="hljs-comment">// 分片文件所在的目录</span><br><span class="hljs-keyword">const</span> chunkDir = <span class="hljs-string">&#x27;path/to/chunk-directory&#x27;</span>;<br><span class="hljs-comment">// 合并后的文件名和目录</span><br><span class="hljs-keyword">const</span> finalFile = <span class="hljs-string">&#x27;path/to/final-file.ext&#x27;</span>;<br><br><span class="hljs-comment">// 读取分片文件夹中的所有分片文件</span><br><span class="hljs-keyword">const</span> chunkFiles = fs.<span class="hljs-title function_">readdirSync</span>(chunkDir);<br><br><span class="hljs-comment">// 按照分片序号进行排序</span><br>chunkFiles.<span class="hljs-title function_">sort</span>(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> <span class="hljs-title class_">Number</span>(a.<span class="hljs-title function_">split</span>(<span class="hljs-string">&#x27;-&#x27;</span>)[<span class="hljs-number">1</span>]) - <span class="hljs-title class_">Number</span>(b.<span class="hljs-title function_">split</span>(<span class="hljs-string">&#x27;-&#x27;</span>)[<span class="hljs-number">1</span>]));<br><br><span class="hljs-comment">// 创建一个可写流，用于写入合并后的文件</span><br><span class="hljs-keyword">const</span> writeStream = fs.<span class="hljs-title function_">createWriteStream</span>(finalFile);<br><br><span class="hljs-comment">// 逐个读取分片文件并写入到合并文件中</span><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; chunkFiles.<span class="hljs-property">length</span>; i++) &#123;<br>  <span class="hljs-keyword">const</span> chunkPath = path.<span class="hljs-title function_">join</span>(chunkDir, chunkFiles[i]);<br><br>  <span class="hljs-comment">// 使用 fs.createReadStream 创建一个可读流</span><br>  <span class="hljs-keyword">const</span> readStream = fs.<span class="hljs-title function_">createReadStream</span>(chunkPath);<br><br>  readStream.<span class="hljs-title function_">on</span>(<span class="hljs-string">&#x27;end&#x27;</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-comment">// 当读取结束后，删除当前分片文件</span><br>    fs.<span class="hljs-title function_">unlinkSync</span>(chunkPath);<br>  &#125;);<br><br>  <span class="hljs-comment">// 将读取的分片文件写入到合并文件中</span><br>  readStream.<span class="hljs-title function_">pipe</span>(writeStream, &#123; <span class="hljs-attr">end</span>: <span class="hljs-literal">false</span> &#125;);<br>&#125;<br><br><span class="hljs-comment">// 在合并完成后，关闭可写流</span><br>writeStream.<span class="hljs-title function_">on</span>(<span class="hljs-string">&#x27;finish&#x27;</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// 合并完成，进行一些后续的操作</span><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;分片合并完成&#x27;</span>);<br>&#125;);<br><br><span class="hljs-comment">// 关闭可写流</span><br>writeStream.<span class="hljs-title function_">end</span>();<br></code></pre></td></tr></table></figure></li>
</ol>
</li>
<li><p>断点续传：</p>
<p>怎么判断网络中断？</p>
<p>它可以检测网络是否中断（心跳机制-定时发送亲求检查网络情况），来决定是否中断请求（如下），并且下一次在中断分片出继续上传，无需从头开始上传（根据上面分片的序列号即可）。</p>
<p>心跳机制：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs js">  <span class="hljs-comment">// 定义心跳请求的URL</span><br>  <span class="hljs-keyword">const</span> heartbeatUrl = <span class="hljs-string">&#x27;https://example.com/heartbeat&#x27;</span>;<br>  <br>  <span class="hljs-comment">// 定义心跳间隔时间（毫秒）</span><br>  <span class="hljs-keyword">const</span> heartbeatInterval = <span class="hljs-number">3000</span>;<br>  <br>  <span class="hljs-keyword">let</span> heartbeatTimer;<br>  <br>  <span class="hljs-comment">// 发送心跳请求</span><br>  <span class="hljs-keyword">function</span> <span class="hljs-title function_">sendHeartbeat</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-title function_">fetch</span>(heartbeatUrl)<br>      .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> &#123;<br>        <span class="hljs-keyword">if</span> (!response.<span class="hljs-property">ok</span>) &#123;<br>          <span class="hljs-title function_">handleNetworkError</span>();<br>        &#125;<br>      &#125;)<br>      .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> &#123;<br>        <span class="hljs-title function_">handleNetworkError</span>();<br>      &#125;);<br>  &#125;<br>  <br>  <span class="hljs-comment">// 处理网络中断</span><br>  <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleNetworkError</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Network connection lost.&#x27;</span>);<br>    <span class="hljs-comment">// 进行其他处理，如中断上传、保存已上传的文件块信息等</span><br>  &#125;<br>  <br>  <span class="hljs-comment">// 开始心跳检测</span><br>  <span class="hljs-keyword">function</span> <span class="hljs-title function_">startHeartbeat</span>(<span class="hljs-params"></span>) &#123;<br>    heartbeatTimer = <span class="hljs-built_in">setInterval</span>(sendHeartbeat, heartbeatInterval);<br>  &#125;<br>  <br>  <span class="hljs-comment">// 停止心跳检测</span><br>  <span class="hljs-keyword">function</span> <span class="hljs-title function_">stopHeartbeat</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-built_in">clearInterval</span>(heartbeatTimer);<br>  &#125;<br>  <br>  <span class="hljs-comment">// 示例：开始心跳检测</span><br><span class="hljs-title function_">startHeartbeat</span>();<br></code></pre></td></tr></table></figure>

<p>如何手动中断请求？</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs js">  <span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;axios&#x27;</span>;<br>  <br>  <span class="hljs-comment">// 创建取消令牌，告诉cancelTokenSource.cancel方法哪个请求要取消</span><br>  <span class="hljs-keyword">const</span> cancelTokenSource = axios.<span class="hljs-property">CancelToken</span>.<span class="hljs-title function_">source</span>();<br>  <br>  <span class="hljs-comment">// 发起请求</span><br>  axios.<span class="hljs-title function_">post</span>(<span class="hljs-string">&#x27;https://example.com/upload&#x27;</span>, file, &#123;<br>    <span class="hljs-attr">cancelToken</span>: cancelTokenSource.<span class="hljs-property">token</span><br>  &#125;)<br>    .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> &#123;<br>      <span class="hljs-comment">// 请求成功的处理逻辑</span><br>    &#125;)<br>    .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">thrown</span> =&gt;</span> &#123;<br>      <span class="hljs-keyword">if</span> (axios.<span class="hljs-title function_">isCancel</span>(thrown)) &#123;<br>        <span class="hljs-comment">// 中断请求的处理逻辑</span><br>      &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-comment">// 其他网络错误的处理逻辑</span><br>      &#125;<br>    &#125;);<br>  <br>  <span class="hljs-comment">// 当需要中断上传时</span><br>cancelTokenSource.<span class="hljs-title function_">cancel</span>(<span class="hljs-string">&#x27;取消上传&#x27;</span>);<br></code></pre></td></tr></table></figure>

<p>结合上面两种方法，实现了大文件上传，既保证了上传速度（分片数据小），又保证了文件的完整性（序列号）</p>
</li>
</ul>
<p><u>思路</u>：slice进行切片-&gt;给每个chunk添加chunk指纹（web worker计算md5，因为hash值计算比较耗时，放在主线程会阻塞）-&gt;上传时询问该chunk是否已上传-&gt;后端用文件流合并；</p>
<p>​           断点续传：由于网络原因自动重传（在catch里面判断是否是Network问题）以及手动暂停（xhr.abort=&gt;axios.cancelToken），再手动重传，重传是记录已经上传的chunk数组，然后重传到所有chunk里遍历，找到没有上传的</p>
</blockquote>
<h3 id="7-如果请求的数据没有过来该怎么办？"><a href="#7-如果请求的数据没有过来该怎么办？" class="headerlink" title="7.如果请求的数据没有过来该怎么办？"></a>7.如果请求的数据没有过来该怎么办？</h3><blockquote>
<p>如果可以不用展示，那就 隐藏，等数据来了再展示</p>
<p>如果必须要展示，可以 设置 默认值</p>
<p>——————-Vue3中的Suspense组件配合插槽 就实现了，有数据和没数据时该如何处理</p>
</blockquote>
<h3 id="8-如何封装一个axios？"><a href="#8-如何封装一个axios？" class="headerlink" title="8.如何封装一个axios？"></a>8.如何封装一个axios？</h3><blockquote>
<ol>
<li>下载axios模块</li>
<li>创建一个axios实例，初始化配置，比如baseUrl</li>
<li>配置请求（config加token等）、相应拦截器（把res用promise返回出去）</li>
<li>export暴露出去</li>
<li>用import引进来封装各个接口（url、method等）并使用</li>
</ol>
</blockquote>
<h1 id="Npm"><a href="#Npm" class="headerlink" title="Npm"></a>Npm</h1><h3 id="1-npm的底层环境是什么？"><a href="#1-npm的底层环境是什么？" class="headerlink" title="1.npm的底层环境是什么？"></a>1.npm的底层环境是什么？</h3><blockquote>
<p>npm是指 node package manage，node包管理器，底层环境是Nodejs</p>
<p>一旦安装了Nodejs，npm也会一并安装，它包含网站—查阅包的地方、命令行—-下载包的地方、注册表—存储包的地方，下载完了便会显示在package.json文件中。</p>
<p><u>思路</u>：是什么-&gt;环境-&gt;npm如何使用一套流程</p>
</blockquote>
<h3 id="2-pnpm相比于npm优势在哪？"><a href="#2-pnpm相比于npm优势在哪？" class="headerlink" title="2.pnpm相比于npm优势在哪？"></a>2.pnpm相比于npm优势在哪？</h3><blockquote>
<ul>
<li><p><strong>更快</strong>：支持并行下载</p>
</li>
<li><p><strong>更节省空间：</strong>同一个包在磁盘中只存在一份，避免包的重复下载</p>
</li>
</ul>
</blockquote>
<h1 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h1><h3 id="1-浏览器存储方式有哪些？"><a href="#1-浏览器存储方式有哪些？" class="headerlink" title="1.浏览器存储方式有哪些？"></a>1.浏览器存储方式有哪些？</h3><blockquote>
<p>Cookie、sessionStorage、localStorage、indexedDB</p>
<p>既然是存储方法，下面说下关于 存多大、存多长时间、存什么内容 它们时间的区别：</p>
<p>大小限制：Cookie（4KB）&lt; 【sessionStorage、localStorage】（5MB）&lt; indexedDB（浏览器上的数据库，比较大）</p>
<p>存储时间：sessionStorage &lt;【 Cookie（任意设置）、localStorage（一直保存在浏览器） 】除非用户或者程序员手动清除&lt; indexedDB （除非手动清除）</p>
<p>存储内容：【Cookie、sessionStorage、localStorage】-&gt;只能字符串，indexedDB-&gt;任意类型</p>
</blockquote>
<h3 id="2-token是存储在cookie中还是localStorage中？"><a href="#2-token是存储在cookie中还是localStorage中？" class="headerlink" title="2.token是存储在cookie中还是localStorage中？"></a>2.token是存储在cookie中还是localStorage中？</h3><blockquote>
<p>什么是token？token是验证身份的令牌，是指用户验证登陆后服务器把凭证经过加密等方式得到的字符串序列</p>
<p>存在cookie中和存在localStorage中有什么不一样？</p>
<p>存在cookie中：登陆后默认会自动将cookie（如果存储在cookie中的话）一同并入请求选项中（想着已经登陆了后续自动帮你配上，不用手动添加token），缺点是会受到跨域限制（怕多个域通信受到攻击），而且还会受到CSRF攻击。</p>
<p>存在localStorage中：需要手动从localStorage获取token并添加至请求选项中，但是会受到XSS攻击。</p>
<p>存在哪里更好？</p>
<p>localStorage，因为存在cookie中，也有被XSS攻击的可能性，因为XSS是js脚本注入，只要通过document.cookie就能拿到，综上所述，存在cookie中，既会受到CSRF攻击又会受到XSS攻击，内存还小，有跨域限制，而存在localStorage中，只有可能有XSS攻击，只要有充足预防措施即可。</p>
<p>为什么不放在sessionStorage中？</p>
<p>因为放在sessionStorage中每个标签数据是独立的，无法实现共享。</p>
<p><u>思路</u>：是什么-&gt;介绍特点-&gt;如何选择</p>
</blockquote>
<h3 id="3-什么是XSS攻击和CSRF攻击以及如何预防？"><a href="#3-什么是XSS攻击和CSRF攻击以及如何预防？" class="headerlink" title="3.什么是XSS攻击和CSRF攻击以及如何预防？"></a>3.什么是XSS攻击和CSRF攻击以及如何预防？</h3><blockquote>
<p>XSS攻击：指跨站脚本，攻击者将恶意脚本注入到受害者网站中，来窃取隐私信息发送到攻击者自己的网站中</p>
<p>预防XSS：既然是诸如脚本，1）一开始可以防止注入，把用户输入的一些字符串（&lt;&gt;等）进行转义2）设置HTTP头中的安全标记，如Content-Security-Policy（CSP），可以指定哪些资源可被加载，从而减少XSS攻击的风险</p>
<p>CSRF攻击：指跨站请求伪装，字面意思就知道 是攻击者伪装成用户发送请求，执行恶意操作，比如利用电子邮件、社交媒体发送链接引诱用户去点击</p>
<p>预防CSRF：在敏感操作的请求中加入CSRF令牌，该令牌是服务器生成的随机值，需要和用户会话关联，攻击者无法伪造</p>
</blockquote>
<h3 id="4-token登录流程？"><a href="#4-token登录流程？" class="headerlink" title="4.token登录流程？"></a>4.token登录流程？</h3><blockquote>
<ol>
<li>客户端输入完账号密码请求登录</li>
<li>服务器收到请求，验证账号密码</li>
<li>验证通过后将凭证经过加密等方式生成token返回给客户端</li>
<li>客户端收到token后，后续的请求都需要带上token</li>
<li>服务器只有验证了token才会返回数据</li>
</ol>
</blockquote>
<h3 id="5-你了解过JWT吗？"><a href="#5-你了解过JWT吗？" class="headerlink" title="5.你了解过JWT吗？"></a>5.你了解过JWT吗？</h3><blockquote>
<p>先说一下为什么会出现这样的技术：对于传统的认证方式来说，服务器端生成完token后还需要在服务器端自己保存一份以便下次验证使用，但是如果在在分布式应用中，一个应用程序的各个服务可能分布在各个服务器上，为了每个服务正常访问，都需要保存一份token信息，这增加了服务器的开销，这是第一点；第二，对于没有保存token信息的服务器来说，等于就闲置了，这就导致了负载不均衡的问题。</p>
<p>而JWT的出现解决了以上的问题：它叫json web token，指在web应用中以json的形式存储认证信息，有如下特点：</p>
<ol>
<li>无状态性：服务端不需要存储验证信息来验证</li>
<li>安全性比较高：JWT是有专门的加密算法</li>
</ol>
<p>下面结合JWT的典型使用场景：单点登录来说明JWT的使用流程和原理：</p>
<p>在单点登录场景中，通常涉及到分布式应用，比如一个企业想要登录一次就可以访问多个子系统，那单点登录会有一个认证中心，所有用户登录后会先在认证中心处进行验证：</p>
<p>那JWT涉及到三个部分：头部、载荷、签名，认证中心验证信息后会对JWT的头部和载荷并通过64base加密并结合私钥生成签名（token）的部分，同时生成一份公钥，并把整个JWT返回给用户，下次用户访问不同的服务都要带上这个JWT，不同的服务获取到JWT先解密，并且结合公钥计算，看最后的结果是否等于签名的值，如果相等，则说明验证通过，说明JWT在传输过程中保持完整，否则回到验证中心去验证。</p>
<p><u>你了解过…吗？答题模版</u></p>
<p><u>知道一个知识点 为什么会有它（为什么），就等于知道了它的定义、应用场景；知道了它的内部实现（如何），就等于深刻理解了它的所有特点</u></p>
</blockquote>
<h3 id="6-页面渲染的过程是什么？"><a href="#6-页面渲染的过程是什么？" class="headerlink" title="6.页面渲染的过程是什么？"></a>6.页面渲染的过程是什么？</h3><blockquote>
<ol>
<li>解析HTML标签生成DOM树</li>
<li>解析CSS样式生成CSSOM</li>
<li>解析js代码，处理事件等和HTML有关联的操作</li>
<li>将DOM树和CSSOM树合成渲染树</li>
<li>根据渲染树计算页面布局（元素的位置和大小）—- 回流</li>
<li>给元素绘制颜色 —- 重绘</li>
<li>将绘制好的元素渲染到页面上</li>
</ol>
</blockquote>
<h3 id="7-DOM树和渲染树有什么区别？"><a href="#7-DOM树和渲染树有什么区别？" class="headerlink" title="7.DOM树和渲染树有什么区别？"></a>7.DOM树和渲染树有什么区别？</h3><blockquote>
<p>最关键的区别就是 DOM树是HTML文档的抽象表示，而渲染树是 最后实际用于渲染页面的树结构，这样一来会带来以下的不同：</p>
<ul>
<li>渲染树不包含像header、meta还有隐藏元素 这样与渲染无关的标签，而DOM树包含</li>
</ul>
</blockquote>
<h3 id="8-说一下浏览器的缓存策略"><a href="#8-说一下浏览器的缓存策略" class="headerlink" title="8.说一下浏览器的缓存策略"></a>8.说一下浏览器的缓存策略</h3><blockquote>
<p>分为两种，强缓存和协商缓存。</p>
<p>强缓存：由服务器端触发，浏览器端收到通知则将html、css、image等缓存下来，下次加载页面直接从缓存中获取</p>
<p>协商缓存：缓存之前需向服务器端发送缓存请求，一旦服务器端同意，才缓存</p>
<p>如何触发？针对HTTP的版本触发方法不一样，如下：</p>
<p>强缓存：</p>
<ul>
<li>HTTP1.0，服务端响应头用 Expires触发，设置浏览器缓存到什么时候</li>
<li>HTTP1.1以及以后，服务端响应头用 CacheControl触发，由于可能服务器在不同国家地区有时差，设置绝对时间不准确，可以用CacheControl设置相对时间，即多长时间内会缓存</li>
</ul>
<p>协商缓存：</p>
<ul>
<li>HTTP1.0，请求头：If-Modified-Since，响应头：Last-Modified，通过匹配修改时间是否一直来判断是否缓存，一致则缓存，否则并不缓存</li>
<li>HTTP1.1以及以后，请求头：If-None-Match，响应头：ETag（Entity，非常笼统的概念，概括为页面的资源，看成一个实体），由于修改时间不一致内容不一定不一致，为了提高性能，直接匹配 修改的值本身，如果值相等，则缓存，否则不缓存</li>
</ul>
<p>协商缓存服务器端会返回304状态码，表示可缓存。</p>
</blockquote>
<h3 id="9-什么是搜索引擎优化（SEO）？"><a href="#9-什么是搜索引擎优化（SEO）？" class="headerlink" title="9.什么是搜索引擎优化（SEO）？"></a>9.什么是搜索引擎优化（SEO）？</h3><blockquote>
<p>搜索引擎优化（SEO）是指提高网页在搜索引擎结果中的排名的一系列技术。</p>
<p>如何优化？从原理上来说，搜索引擎分析的是HTML标签的内容和格式，只要 能帮助搜索引擎分析HTML的都可以优化，有如下几种方式（暂时）：</p>
<ul>
<li>优化关键词，即 内容尽量包含 搜索较多的关键词，便于搜索引擎直接获取想要的内容</li>
<li>优化HTML内容，即 HTML段落的内容尽量有层次，便于搜索引擎分析</li>
<li>优化HTML结构，尽量使用语义化标签（见上面），便于搜索引擎分析</li>
</ul>
</blockquote>
<h3 id="10-浏览器的安全机制是什么？"><a href="#10-浏览器的安全机制是什么？" class="headerlink" title="10.浏览器的安全机制是什么？"></a>10.浏览器的安全机制是什么？</h3><blockquote>
<p>基本上有以下四种安全机制：</p>
<ol>
<li>同源策略：是浏览器最基本的安全机制，用来限制不同源（协议、域名、端口）之间的网络请求，减少了被攻击的可能性</li>
<li>HTTPS协议：相对于HTTP本质上是加了一层SSL的加密协议，使得客户端和服务端在传输过程中是不可见的，即使被第三方拦截，也无法被获取</li>
<li>预防XSS和CSRF攻击：见上面</li>
<li>沙盒机制：<ul>
<li>为什么叫沙盒呢？是说它把各个标签页看成沙盒一样，相互隔离，不通标签页是相互独立的，一个标签碰到问题了也不会影响整个浏览器</li>
<li>然后，它也能针对当前的标签页对电脑数据的访问进行限制，比如文件系统、系统设置</li>
<li>接着还能对当前页允许访问的资源进行资源使用限制，防止网页崩溃</li>
</ul>
</li>
</ol>
</blockquote>
<h1 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h1><h3 id="1-Webpack具体做了什么事情，各有什么好处？"><a href="#1-Webpack具体做了什么事情，各有什么好处？" class="headerlink" title="1.Webpack具体做了什么事情，各有什么好处？"></a>1.Webpack具体做了什么事情，各有什么好处？</h3><blockquote>
<ol>
<li>生成依赖图：webpack会找到入口文件（Vue中就是main.js），分析代码中的依赖关系，最终生成依赖图，此依赖图展现了各个模块以及依赖关系</li>
<li>代码转换：对于每个模块，会利用各种加载器（loader），比如babel-loader可将ES6语法-&gt;ES5语法（CommonJs）、scss-loader可将Scss等预处理文件-&gt;Css文件、url-loader可将图片URL-&gt;dataURL（转换为base64编码）</li>
<li>代码分割（可选）：可能模块比较多，代码分割可以将代码按模块分类单独放在各个文件里，实现按需加载，比如按需加载首屏加载所需代码，提高首屏加载速度</li>
<li>资源优化：会对各个模块的代码进一步的优化，比如代码压缩合并（针对单个模块，去除换行、空格、把公共代码单独放入一个文件）等，目的是减少单个文件体积，提高加载速度</li>
<li>模块打包：如果不进行代码分割，按理来说所有的js代码都会打包在bundle.js文件里，其中bundle.js文件会包含渲染函数（render函数），Vue在加载应用时就会执行render函数来渲染页面</li>
</ol>
</blockquote>
<h3 id="2-Webpack打包后主要生成了哪些文件？"><a href="#2-Webpack打包后主要生成了哪些文件？" class="headerlink" title="2.Webpack打包后主要生成了哪些文件？"></a>2.Webpack打包后主要生成了哪些文件？</h3><blockquote>
<ol>
<li>index.html：项目主要的HTML文件，也就是单页面应用的主页面</li>
<li>图片和其他静态资源（字体）</li>
<li>bundle.js以及其他模块js文件</li>
<li>css样式文件</li>
</ol>
<p>在index.html里会引入图片、bundle.js、css文件来加载应用</p>
</blockquote>
<h3 id="3-Webpack打包和不打包的区别？"><a href="#3-Webpack打包和不打包的区别？" class="headerlink" title="3.Webpack打包和不打包的区别？"></a>3.Webpack打包和不打包的区别？</h3><blockquote>
<p>webpack打包后有以下优点是不打包所没有额度</p>
<ol>
<li><p>更好地支持</p>
<p>webpack在第一阶段会代码转换，利用babel等工具把一些语法转换为浏览器能识别的语法，会将ES6语法-&gt;ES5语法（CommonJs）、Scss等预处理文件-&gt;Css文件、图片-&gt;dataURL（转换为base64编码）</p>
</li>
<li><p>提高加载速度</p>
<p>webpack在代码分割、资源优化阶段可以大大减小包的大小，从而提高加载速度</p>
</li>
</ol>
</blockquote>
<h3 id="4-Webpack的基本配置文件（webpack-config-js）中的entry、output、module、plugins等核心选项分别用来做什么？"><a href="#4-Webpack的基本配置文件（webpack-config-js）中的entry、output、module、plugins等核心选项分别用来做什么？" class="headerlink" title="4.Webpack的基本配置文件（webpack.config.js）中的entry、output、module、plugins等核心选项分别用来做什么？"></a>4.Webpack的基本配置文件（webpack.config.js）中的entry、output、module、plugins等核心选项分别用来做什么？</h3><blockquote>
<p>entry：入口文件，可以为一个或多个</p>
<p>output：输出配置，比如路径</p>
<p>module：配置loader处理css文件、style文件等，优化构建速度，比如css-loader、style-loader、file-loader，一句话：帮助webpack处理相应文件的</p>
<p>plugins：用一些第三方的插件做一些辅助性的处理，比如CleanWebpackPlugin，可以每次构建前清空构建目录</p>
</blockquote>
<h3 id="5-如何通过Webpack配置实现代码拆分（code-splitting）等优化和懒加载（lazy-loading）？"><a href="#5-如何通过Webpack配置实现代码拆分（code-splitting）等优化和懒加载（lazy-loading）？" class="headerlink" title="5.如何通过Webpack配置实现代码拆分（code splitting）等优化和懒加载（lazy loading）？"></a>5.如何通过Webpack配置实现代码拆分（code splitting）等优化和懒加载（lazy loading）？</h3><blockquote>
<p><strong>Webpack的所有优化都是在optimization配置项下</strong>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-attr">optimization</span>:&#123;<br>    <span class="hljs-comment">// 代码分割</span><br>    <span class="hljs-attr">splitChunks</span>:&#123;<br>        <span class="hljs-attr">cacheGroups</span>:&#123;<br>            <span class="hljs-comment">// 提取公共模块</span><br>            <span class="hljs-attr">common</span>:&#123;chunk名字以及目录&#125;,<br>            <span class="hljs-comment">// 按照组件分块</span><br>            <span class="hljs-attr">components</span>:&#123;chunk名字以及目录&#125;<br>        &#125;<br>    &#125;,<br>    <span class="hljs-comment">// Tree Shaking</span><br>    <span class="hljs-attr">minimize</span>:<span class="hljs-literal">true</span>,<br>    <span class="hljs-comment">// 压缩Js和Css文件,注意terserPlugin需要安装webpack对应版本，比如webpack4-&gt;terserPlugin4,webpack默认安装5版本的</span><br>    <span class="hljs-attr">minimizer</span>:&#123;<br>        <span class="hljs-keyword">new</span> <span class="hljs-title function_">terserPlugin</span>(&#123;<br>            <span class="hljs-comment">// 缓存js文件</span><br>            <span class="hljs-attr">cache</span>:<span class="hljs-literal">true</span>，<br>            <span class="hljs-attr">terserOptions</span>:&#123;<br>               <span class="hljs-comment">// 去除console、debugger语句</span><br>               <span class="hljs-attr">compress</span>:&#123;<br>                  <span class="hljs-attr">drop_console</span>:<span class="hljs-literal">true</span><br>                  <span class="hljs-attr">drop_debugger</span>:<span class="hljs-literal">true</span><br>               &#125;<br>            &#125;<br>        &#125;),<br>        <span class="hljs-keyword">new</span> <span class="hljs-title class_">OptimizeCSSAssetsPlugin</span>(&#123;<br>            <span class="hljs-comment">// 有三种模式default(去除空格等)、medium(浏览器兼容,比如加上其他浏览器前缀,谷歌和Safari都是-webkit-,而火狐是-moz-)、advanced(针对重复选择器去重等)</span><br>            <span class="hljs-attr">cssProcessorPluginOptions</span>: &#123;<br>                 <span class="hljs-attr">preset</span>: [<span class="hljs-string">&#x27;default&#x27;</span>, &#123; <span class="hljs-attr">discardComments</span>: &#123; <span class="hljs-attr">removeAll</span>: <span class="hljs-literal">true</span> &#125; &#125;]<br>              &#125;<br>        &#125;),<br>        <span class="hljs-comment">// 需要配合服务端配置才可以(ngix)</span><br>        <span class="hljs-keyword">new</span> <span class="hljs-title class_">CompressWebpackPlugin</span>(&#123;<br>            <span class="hljs-attr">test</span>:<span class="hljs-regexp">/\.(js|html|css)$/</span>,压缩的文件类型<br>            <span class="hljs-attr">threhold</span>:<span class="hljs-number">10240</span>,<span class="hljs-comment">//单位B,超过10KB就进行压缩</span><br>        &#125;)<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><strong>懒加载：</strong></p>
<p>一旦代码中使用了import的语法，webpack自动会进行懒加载处理</p>
</blockquote>
<h3 id="6-Webpack中的哪些配置选项或插件可以帮助提高构建速度？"><a href="#6-Webpack中的哪些配置选项或插件可以帮助提高构建速度？" class="headerlink" title="6.Webpack中的哪些配置选项或插件可以帮助提高构建速度？"></a>6.Webpack中的哪些配置选项或插件可以帮助提高构建速度？</h3><blockquote>
<p>在module配置项中添加thread-loader可开启多线程处理</p>
<p>还有一些css-loader、style-loader、file-loader</p>
</blockquote>
<h3 id="7-你对Vite构建工具有哪些了解？它与Webpack相比有什么优势？"><a href="#7-你对Vite构建工具有哪些了解？它与Webpack相比有什么优势？" class="headerlink" title="7.你对Vite构建工具有哪些了解？它与Webpack相比有什么优势？"></a>7.你对Vite构建工具有哪些了解？它与Webpack相比有什么优势？</h3><blockquote>
<p>Vite是新一代前端构建工具，相较于Webpack，它省去了 代码转化、分析以及打包的动作，直接按需加载编译服务器的代码，所以会更快</p>
</blockquote>
<h1 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h1><h3 id="1-v-show和v-if的区别"><a href="#1-v-show和v-if的区别" class="headerlink" title="1.v-show和v-if的区别"></a>1.v-show和v-if的区别</h3><blockquote>
<p>两者都是用来显示隐藏元素的，区别如下：</p>
<ol>
<li>从原理上来看：v-show是通过控制display的值来显示和隐藏的；而v-if是通过对DOM元素的添加和删除实现显示和隐藏的</li>
<li>从对象上来说：<ul>
<li>既然v-if是直接对DOM操作的，性能较低，效率低，而v-show效率高</li>
<li>如果DOM标签包含事件，v-if的显示和隐藏会涉及事件的绑定以及解绑；如果DOM标签包含组件，v-if还会涉及到组件的编译、实例化、渲染以及挂载。而v-show仅仅是简单的CSS样式切换。</li>
</ul>
</li>
</ol>
</blockquote>
<h3 id="2-如何理解MVVM的？"><a href="#2-如何理解MVVM的？" class="headerlink" title="2.如何理解MVVM的？"></a>2.如何理解MVVM的？</h3><blockquote>
<p>它是一种软件架构（相当于文章的大纲）模式，全称是Model-View-ViewModel，这三个部分实现了</p>
<ol>
<li>前端可以临时存储数据（前后端分离之前都是用的MVC模式，数据只能依赖后端，通过C-Controller分发数据）</li>
<li>将数据和视图进行双向绑定：用户可以修改View数据，ViewModel可监听到并通知Model数据的修改；同时，ViewModel也可以将Model数据的修改更新到View上。整个过程是自动地，无需认为操作。</li>
</ol>
<p>Ps：这个问题后续估计会提问：v-model的实现原理（MVVM的具体技术）、响应式原理，面试官不问就不用说那么多，对于MVVM就讲个 定义-&gt;历史（与MVC的关系）-&gt;功能。</p>
</blockquote>
<h3 id="3-v-for中的key有什么作用？"><a href="#3-v-for中的key有什么作用？" class="headerlink" title="3.v-for中的key有什么作用？"></a>3.v-for中的key有什么作用？</h3><blockquote>
<p>key是DOM元素的唯一标识</p>
<p>作用：主要就是用来辅助虚拟DOM比对新旧节点来复用DOM元素，提高性能，否则就会胡乱地就地复用，增加不必要的重新渲染。</p>
<p>使用key的注意点：要确保key值唯一，并且不要使用自带的index作为key值（仍然会出现胡乱地就地复用，比如列表原来index 0-1-2，在首部添加一个元素，会把后面的元素往后顶，但index值不变，0-1-2-3，出现了混乱复用）</p>
<p><u>思路</u>：在回答xxx有什么作用时，模板：定义-&gt;作用（先解释是什么，再说有什么作用）-&gt;如何使用</p>
</blockquote>
<h3 id="4-说一下你对生命周期的理解"><a href="#4-说一下你对生命周期的理解" class="headerlink" title="4.说一下你对生命周期的理解"></a>4.说一下你对生命周期的理解</h3><blockquote>
<ol>
<li>beforeCreate：实例创建之前，此时 数据、方法无法访问</li>
<li>created：实例创建完成，已经完成了数据监测（数据劫持–数据的准备工作），可以修改数据但是不会更新到页面，因为还没挂载</li>
<li>beforeMount：挂载之前，完成了模板编译（具体见下面），生成了虚拟DOM，即将渲染页面</li>
<li>mounted：页面已经渲染完成并挂载到页面，可以访问DOM元素也可以发送异步请求（目的是得到数据给页面展示，由于页面已经初始化就可以把数据放心放在页面了–但是可能会卡顿，见下面）</li>
<li>beforeUpdate：页面更新之前，数据是新的，但页面是旧的</li>
<li>Updated：页面已经更新完毕，此时不要修改数据，已经为时已晚</li>
<li>beforeDestory：销毁之前，这里可以销毁定时器等</li>
<li>destoryed：已销毁</li>
</ol>
<p>keep-alive组件缓存专属：</p>
<ul>
<li>activiated：激活时</li>
<li>deactiviated：销毁时</li>
</ul>
</blockquote>
<h3 id="5-模板编译的原理"><a href="#5-模板编译的原理" class="headerlink" title="5.模板编译的原理"></a>5.模板编译的原理</h3><blockquote>
<ol>
<li>解析模板生成AST（抽象语法树 Abstract Async Tree），它是一种树状结构，描述了模板中模板字符串、指令等模板语法之间的关系</li>
<li>对AST进行静态标记（Vue3才提出来的内容），指出哪些元素是静态的，告知虚拟DOM哪些元素不需进行比对，为虚拟DOM的工作减轻了负担</li>
<li>针对AST生成render函数，一旦执行render函数，就会生成虚拟DOM</li>
</ol>
</blockquote>
<h3 id="6-Vue3生命周期有哪些变化？"><a href="#6-Vue3生命周期有哪些变化？" class="headerlink" title="6.Vue3生命周期有哪些变化？"></a>6.Vue3生命周期有哪些变化？</h3><blockquote>
<ol>
<li>用setup函数代替了beforeCreate和created（如果要打印sepup函数和beforeCreate，sepup比beforeCreate还先执行）</li>
<li>其他函数前面都加上了on，更易于理解</li>
<li>beforeDestory、destoryed -&gt;onBeforeUnMounted、onUnMounted，与挂载一一对应</li>
</ol>
</blockquote>
<h3 id="7-父子生命周期函数的执行顺序"><a href="#7-父子生命周期函数的执行顺序" class="headerlink" title="7.父子生命周期函数的执行顺序"></a>7.父子生命周期函数的执行顺序</h3><blockquote>
<ul>
<li><p>实例化、挂载</p>
<p>父beforeCreate-&gt;父created-&gt;父beforeMount-&gt;子beforeCreate-&gt;子created-&gt;子beforeMount-&gt;子mounted-&gt;父mounted（必须要实例所有内容包括子组件准备完毕才能挂载）</p>
</li>
<li><p>父组件更新</p>
<p>父beforeUpdate-&gt;父Updated</p>
</li>
<li><p>子组件更新</p>
<p>父beforeUpdate-&gt;子beforeUpdate-&gt;子Updated-&gt;父Updated</p>
</li>
<li><p>销毁</p>
<p>父beforeDestory-&gt;子beforeDestory-&gt;子destoryed -&gt;父destoryed </p>
</li>
</ul>
<p><u>原则</u>：子组件是父组件的一部分，因此碰到子组件会连带父组件，反过来不会</p>
</blockquote>
<h3 id="8-在created和mounted中请求数据有什么不同？"><a href="#8-在created和mounted中请求数据有什么不同？" class="headerlink" title="8.在created和mounted中请求数据有什么不同？"></a>8.在created和mounted中请求数据有什么不同？</h3><blockquote>
<p>created：在页面渲染之前</p>
<p>mounted：在页面渲染之后</p>
<p>使用场景：如果请求的数据 和DOM有关—-&gt;放在created中</p>
<p>​                                               和DOM无关—–&gt;放在mounted中</p>
<p>推荐放在created，有以下理由：</p>
<ul>
<li>更早地获取到服务端数据，减少loading时间（卡顿）</li>
<li>服务端渲染SSR（见下面）不支持mounted，可以保持统一性</li>
</ul>
</blockquote>
<h3 id="9-什么是服务端渲染（SSR）？"><a href="#9-什么是服务端渲染（SSR）？" class="headerlink" title="9.什么是服务端渲染（SSR）？"></a>9.什么是服务端渲染（SSR）？</h3><blockquote>
<p>就是 将浏览器端执行js代码的工作 放在服务端来做，服务端处理完，返回HTML字符串给浏览器，这么做有以下好处：</p>
<ul>
<li>浏览器减少了负担，加快了首屏加载速度</li>
<li>由于页面加载速度快了，也有助于SEO</li>
</ul>
</blockquote>
<h3 id="10-Vue中修饰符有哪些？"><a href="#10-Vue中修饰符有哪些？" class="headerlink" title="10.Vue中修饰符有哪些？"></a>10.Vue中修饰符有哪些？</h3><blockquote>
<ol>
<li>事件修饰符<ul>
<li>.capture：事件捕获模式，先触发当前事件再触发内部事件</li>
<li>.self：只会触发当前事件，不会被事件传播而影响被触发</li>
<li>.stop：阻止事件冒泡</li>
<li>.once：有且仅触发一次</li>
<li>.passive（消极-任由继续往后找）：不阻止/触发默认行为</li>
<li>.prevent：阻止默认行为</li>
<li>.native：仅触发原生事件，这些事件Vue没有，因为不常用</li>
</ul>
</li>
<li>v-model修饰符<ul>
<li>.number：将输入转化为数字</li>
<li>.trim：出入完去掉空格</li>
<li>.lazy：可以等输入完再触发事件（请求），类似函数防抖</li>
</ul>
</li>
<li>键盘修饰符<ul>
<li>keyup</li>
<li>keydown</li>
</ul>
</li>
<li>系统修饰符<ul>
<li>.ctrl</li>
<li>.alt</li>
</ul>
</li>
<li>鼠标修饰符<ul>
<li>.left</li>
<li>.middle</li>
<li>.right</li>
</ul>
</li>
</ol>
<p><u>思路</u>：事件修饰符（寻找亲戚：事件传播-近邻，默认行为-远亲，原生-远远亲）-&gt;键盘相关（表单（输入是为了触发请求，而lazy可以懒触发）-&gt;特殊）-&gt;鼠标</p>
</blockquote>
<h3 id="11-elementUI怎么做表单验证的？"><a href="#11-elementUI怎么做表单验证的？" class="headerlink" title="11.elementUI怎么做表单验证的？"></a>11.elementUI怎么做表单验证的？</h3><blockquote>
<ul>
<li>内置规则：在表单添加rules属性，rules中的每个表单项规则按照文档来</li>
<li>自定义规则：比如自己写个邮件验证的规则，然后放入validator属性中</li>
</ul>
</blockquote>
<h3 id="12-Vue中的组件通信有哪些？"><a href="#12-Vue中的组件通信有哪些？" class="headerlink" title="12.Vue中的组件通信有哪些？"></a>12.Vue中的组件通信有哪些？</h3><blockquote>
<ul>
<li><p>父子通信</p>
<p>props：父-&gt;子，$emit：子-&gt;父</p>
<p>$parent、$children：强制性获取父组件、子组件们（$children是个数组而$parent不是）的实例进行通信，获取到实例的属性或方法可以互传</p>
<p>$refs：几乎等同于$children，相比$children，优点是可以针对性获取实例，独立性更高</p>
</li>
<li><p>跨级通信</p>
<p>$attrs、$listeners：为了解决利用props的A-&gt;B-&gt;C逐层传递的臃肿问题而出现，A可以把多个数据、回调同时传递给B，B直接再把$attrs、$listeners传递给C即可，不需要先接受再命名传递（注意$attrs是除了B的props以外的数据，因为A可能有专门传递给B用的数据）</p>
<p>provide、inject：只能由上往下跨级传递，不能由下往上且不能同级传递，需统一数据流方向，否则不方便维护</p>
</li>
<li><p>同级通信</p>
<p>eventBus/发布订阅：和provide、inject一样有维护不便的问题</p>
<p>vuex</p>
</li>
</ul>
</blockquote>
<h3 id="13-为什么Vue中尽量使用箭头函数而不是传统函数？"><a href="#13-为什么Vue中尽量使用箭头函数而不是传统函数？" class="headerlink" title="13.为什么Vue中尽量使用箭头函数而不是传统函数？"></a>13.为什么Vue中尽量使用箭头函数而不是传统函数？</h3><blockquote>
<p>因为传统函数会有this指向问题，即按照原来的this指向规则可能会指向到原本不想要的对象上，因此直接使用箭头函数，取消当前函数的this，按照外部Vue实例内部来访问。</p>
</blockquote>
<h3 id="14-keep-alive组件是什么？如何使用的"><a href="#14-keep-alive组件是什么？如何使用的" class="headerlink" title="14.keep-alive组件是什么？如何使用的?"></a>14.keep-alive组件是什么？如何使用的?</h3><blockquote>
<p>它是Vue内置组件，可以对组件进行缓存，在组件进行切换时不需要销毁。</p>
<p>用keep-alive包裹组件，并利用include、exclude指定哪些组件需要缓存和不需要缓存的组件</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;keep-live :include=组件数组 :exclude=组件数组&gt;<br>...<br>&lt;/keep-live&gt;<br></code></pre></td></tr></table></figure>

<p>好处就是 缓存活跃的组件，不需要重新渲染，提高性能以及用户体验</p>
</blockquote>
<h3 id="15-Vue路由怎么传参的？"><a href="#15-Vue路由怎么传参的？" class="headerlink" title="15.Vue路由怎么传参的？"></a>15.Vue路由怎么传参的？</h3><blockquote>
<ul>
<li><p>params参数：用于传递一些必要的参数，没有这个参数路由都无法匹配，例如用户ID，商品ID，必须要这个参数才能正常加载组件的</p>
<p>需要在路由配置动态URL片段，如/user/:id，刷新后params参数会丢失，因为刷新后路由初始化，路由都无法匹配了，参数当然也没了</p>
</li>
<li><p>query参数：用于传递一些非必要的参数，比如搜索框的关键字，可以传也可以不传，不传就搜索所有数据</p>
<p>刷新后路由能正常匹配，于是查询字符串的query参数也能保存，类似于get参数会显示在URL上</p>
<p>params参数、query参数都可以通过对象的形式传参，获取方式也类似</p>
</li>
<li><p>props传参：用于传递组件自身需要的一些复杂属性，比如父子通过路由传值</p>
<p>要在路由文件中加上props，再在子组件中用props接受</p>
</li>
</ul>
</blockquote>
<h3 id="16-什么是单页面应用（SPA）？"><a href="#16-什么是单页面应用（SPA）？" class="headerlink" title="16.什么是单页面应用（SPA）？"></a>16.什么是单页面应用（SPA）？</h3><blockquote>
<p>是指整个应用只有一个HTML文件，页面的内容会根据用户的交互动态地呈现。</p>
<p>单页面应用（SPA）主要是利用前端路由来实现的：通过监听路由的URL变化来加载不同的组件，从而实现动态地更新内容。</p>
</blockquote>
<h3 id="17-Vue中的hash模式和history模式有什么区别？"><a href="#17-Vue中的hash模式和history模式有什么区别？" class="headerlink" title="17.Vue中的hash模式和history模式有什么区别？"></a>17.Vue中的hash模式和history模式有什么区别？</h3><blockquote>
<p><strong>从原理上来看：</strong></p>
<p>hash模式是通过hashChange事件来监听URL的hash值（#以及后面的，会存储在location.hash中）的变化来匹配路由规则并加载对应组件的（路由规则以及组件资源在浏览器端的缓存中-bundle.js文件，匹配时会自动去掉#），这个过程是在浏览器端进行的，不会向服务器发送HTTP请求</p>
<p>而history模式是通过popState事件（历史记录栈顶状态的变化）来监听历史记录的变化（最终获取的是pathname）来匹配路由规则并加载对应组件的（后面和hash模式一样，仅仅是监听的东西不一样），其中，在监听之前，是通过H5的API（H5才出的，因此兼容性没有hash模式好）：pushState（）和replaceState来存储并修改历史记录为监听历史记录做准备</p>
<p><strong>从用途上来看：</strong></p>
<p>hash模式始终都不会向服务器发送请求（除了请求组件需要的数据），单页面应用也倾向于使用Hash模式———适合在项目准备上线时设置</p>
<p>而history模式是 在刷新页面或者手动输入URL 时会向服务器请求资源并重复覆盖浏览器的缓存资源，入宫没有对应的资源，会出现404，但是可以在后端配置返回什么，就可以避免404—————-适合在开发阶段测试数据时使用</p>
</blockquote>
<h3 id="18-路由是怎么拦截的？"><a href="#18-路由是怎么拦截的？" class="headerlink" title="18.路由是怎么拦截的？"></a>18.路由是怎么拦截的？</h3><blockquote>
<p>在哪拦截：路由守卫中</p>
<p>如何拦截？</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs vue">// 1.路由配置中在meta添加自定义属性来判断<br>&#123;<br>path:&#x27;index.html&#x27;,<br>name:&#x27;index&#x27;,<br>mata:&#123;<br>  reuqiredAuth:true  <br>&#125;<br>&#125;<br>// 2.路由守卫中判断<br>router.beforeEach((from,to,next)=&gt;&#123;<br> if(this.route.meta.reuqiredAuth)&#123;<br>   if(store.state.token)&#123;<br>     next()<br>  &#125;<br>&#125;<br>&#125;)<br></code></pre></td></tr></table></figure>
</blockquote>
<h3 id="19-如果路由中一个路由对象同时有redirect和component，Vue-Router会如何处理？"><a href="#19-如果路由中一个路由对象同时有redirect和component，Vue-Router会如何处理？" class="headerlink" title="19.如果路由中一个路由对象同时有redirect和component，Vue Router会如何处理？"></a>19.如果路由中一个路由对象同时有redirect和component，Vue Router会如何处理？</h3><blockquote>
<p>会优先使用redirect的组件，因为有重定向会立刻导航并加载重定向路径的组件，而忽视当前的组件</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs vue">const routes = [<br>&#123;<br> path: &#x27;/example&#x27;,<br> component: ExampleComponent, // 示例组件<br> redirect: &#x27;/redirected&#x27; // 重定向路径<br>&#125;,<br>&#123;<br> path: &#x27;/redirected&#x27;,<br> component: RedirectedComponent // 重定向后显示的组件<br>&#125;<br>]<br></code></pre></td></tr></table></figure>

<p>会加载RedirectedComponent</p>
</blockquote>
<h3 id="20-路由有哪些守卫钩子，又是如何解析的？"><a href="#20-路由有哪些守卫钩子，又是如何解析的？" class="headerlink" title="20.路由有哪些守卫钩子，又是如何解析的？"></a>20.路由有哪些守卫钩子，又是如何解析的？</h3><blockquote>
<p><strong>分类</strong></p>
<p>先说下所有的钩子函数：全局守卫、路由独享路由、组件守卫。</p>
<p><strong>完整的导航解析流程</strong></p>
<ul>
<li>导航被触发，比如router.push()、router.replace()</li>
<li>调用失活组件的beforeRouteLeave守卫</li>
<li>调用全局前置守卫beforeEach，在这可以添加一些登录验证，这是验证是全局的，也就是它针对所有路由</li>
<li>调用重用组件的beforeRouteUpdate守卫，这个守卫专门在路由 的组件复用时调用，比如两个路由地址不同，但是对应组件是一样的，如果有路由的组件复用，调用完该守卫更新完毕就不会调用后面的守卫了</li>
<li>调用路由独享的守卫beforeEnter守卫，路由独享的守卫就这一个，专门为跳转当前路由之前做一些逻辑，比如也可以做登录验证，只是这个验证只针对当前路由</li>
<li>下面到了异步组件（使用import关键字的懒加载组件）的解析阶段</li>
<li>调用激活组件的beforeRouteEnter守卫，在该守卫中还没有实例化，无法使用this，可准备好异步数据给激活的组件</li>
<li>调用全局解析守卫beforeResolve，该守卫是解析的最后一步，也可以获取异步数据，只是这个数据一般是可能其他组件也要使用，比如用户的信息放入仓库</li>
<li>导航被确认。为什么要确认一下？表示要进入 下面路由的信息已经准备好。</li>
<li>调用全局后置守卫afterEach，目标路由已经跳转结束，可处理一些收尾工作，比如跳转到目标页面的对应位置</li>
<li>目标组件DOM渲染</li>
<li>调用全局前置守卫beforeRouteEnter的回调函数next()，调用此函数时已经实例化，并把实例作为参数传递给组件路由的下一阶段使用（意思就是说组件已经渲染好了进入该组件的钩子也该结束了）</li>
</ul>
</blockquote>
<h3 id="21-说一下Vue的动态路由"><a href="#21-说一下Vue的动态路由" class="headerlink" title="21.说一下Vue的动态路由"></a>21.说一下Vue的动态路由</h3><blockquote>
<p>动态路由是指加了权限控制的路由，最终实现的效果是：不同用户登陆后，显示不同的菜单功能，因为不同用户有不同权限</p>
<p>如何实现，大致分以下几步：</p>
<ol>
<li>登陆后获取用户的权限信息，比如角色信息</li>
<li>在路由配置中提前写好静态路由（不需要权限就可以访问的）和预制动态路由（admin所有能访问的路由），预制动态路由中必须要加上meta属性，包括icon（菜单图标）、title（菜单标题）、role（角色）等</li>
<li>用获取到的用户权限信息到预制动态路由中匹配，去找当前角色能访问的菜单功能，并最终重新生成一个新的路由</li>
<li>利用新的路由进行循环展示sideBar</li>
</ol>
</blockquote>
<h3 id="22-如何解决动态路由在刷新后二次加载路由（动态路由做权限管理碰到的问题）？"><a href="#22-如何解决动态路由在刷新后二次加载路由（动态路由做权限管理碰到的问题）？" class="headerlink" title="22.如何解决动态路由在刷新后二次加载路由（动态路由做权限管理碰到的问题）？"></a>22.如何解决动态路由在刷新后二次加载路由（动态路由做权限管理碰到的问题）？</h3><blockquote>
<p>使用动态路由做权限管理时通常会出现两个问题：</p>
<ul>
<li><p>动态路由添加后刷新 出现404/白屏</p>
<p><strong>原因：</strong>本来登录后把添加的路由会放入vuex中，然后在跳转首页时在beforeEnter里面会使用router.addRoute配合循环（vue2的addRoutes已经弃用了）动态添加路由，然后next()放行并展示 添加的菜单。但是 刷新后 Vuex数据会丢失，于是再进入新添加的路由，进入beforeEnter钩子时获取不到添加的路由地址，于是会出现404/白屏</p>
</li>
</ul>
<p>​       <strong>解决办法：</strong>在beforeEnter中加一个判断，如果vuex的路由为空，则重新生成一下动态路由，再重新添加。但是，要注意的是，由于 <strong>路由是动态添加的，而路由匹配是在beforeEnter之前做的事情</strong>，因此next放行是需要使用next({ …to, replace: true })：循环调用beforeEnter直到动态路由 成功添加后 并 匹配到地址后，再放行，其中replace是指不希望用户后退使用后退操作，因为可能前一个页面也是动态添加的，否则vuex的路由不为空（生成了路由）且to的地址不为空（成功添加了路由），才放行。</p>
<ul>
<li>不同用户登录后，动态路由会重复添加</li>
</ul>
<p>​       <strong>原因：</strong>不同用户登录后前一个用户动态添加的路由不会删除</p>
<p>​       <strong>解决办法：在动态添加路由之前使用空的路由对象的matcher替换当前路由对象的matcher</strong></p>
<p>​       router.matcher = new Router({ mode: ‘history’, base: ‘s-connect’, routes: [] }).matcher这么做的效果是：把路由配置可以重新替换而不是追加。</p>
</blockquote>
<h3 id="23-Vuex数据刷新后会丢失吗？怎么解决？"><a href="#23-Vuex数据刷新后会丢失吗？怎么解决？" class="headerlink" title="23.Vuex数据刷新后会丢失吗？怎么解决？"></a>23.Vuex数据刷新后会丢失吗？怎么解决？</h3><blockquote>
<p>会，因为Vuex中的数据是放在运行时内存中的，一旦刷新就得重新初始化。</p>
<p><strong>解决方法</strong>：使用浏览器缓存来存储，比如localStorage，一旦在Vuex获取到数据后便放入localStorage，然后监听浏览器刷新事件，一旦刷新了，在里面就用localStorage的数据</p>
</blockquote>
<h3 id="24-Watch和computed的区别？"><a href="#24-Watch和computed的区别？" class="headerlink" title="24.Watch和computed的区别？"></a>24.Watch和computed的区别？</h3><blockquote>
<p>要看区别主要要从 用途上以及过程上来看：<strong>computed是来计算data中的属性的，而watch是来监听属性并处理一些逻辑的。</strong></p>
<ol>
<li>computed函数需要return，而watch不用，因为它不一定需要返回展示</li>
<li>页面初始化时，computed是会计算的，而watch并不会，因为它不需要像computed立马需要展示computed不支持异步，还是一样需要赶快展示，而watch执行的回调本来就是请求数据去的，这就是异步的</li>
<li>computed支持缓存，即数据变化时才会重新计算（为了性能），而watch不会，因为依赖用户的交互值，每次都会变</li>
</ol>
<p><u>思路</u>：用途+使用的时间顺序</p>
</blockquote>
<h3 id="25-Vuex在什么场景下会使用？有什么属性？"><a href="#25-Vuex在什么场景下会使用？有什么属性？" class="headerlink" title="25.Vuex在什么场景下会使用？有什么属性？"></a>25.Vuex在什么场景下会使用？有什么属性？</h3><blockquote>
<p>场景：在需要数据共享的时候，比如用户信息</p>
<p>包括以下属性：</p>
<ul>
<li>Actions：请求异步数据，执行mutations函数</li>
<li>Mutations：直接修改state中的数据</li>
<li>State:存储数据的地方</li>
<li>Getters：state数据的计算属性</li>
</ul>
</blockquote>
<h3 id="26-Vue双向数据绑定原理是什么（100-会问）？"><a href="#26-Vue双向数据绑定原理是什么（100-会问）？" class="headerlink" title="26.Vue双向数据绑定原理是什么（100%会问）？"></a>26.Vue双向数据绑定原理是什么（100%会问）？</h3><blockquote>
<p><strong>1、先说一下双向绑定的组成部分：</strong></p>
<p>它包含view、model、view model三个部分，其中最重要的就是view model，使它实现的view和model的关联，双向绑定的原理最主要的就是view model的原理。</p>
<p>那view model它又包含两个部分，编译器和监听器，编译器实现view的变化影响model的变化，而model的变化影响view的变化是需要 【监听器+编译器】 一起实现的，后者其实就是指的是响应式。</p>
<p><strong>2、下面说响应式是怎么实现的（要用到监听器和编译器）：</strong></p>
<p>一开始该组件的的vue实例在初始化时对data数据进行响应式处理，这个过程发生在监听器中，在defineReactive中的Object.defineproperty进行数据劫持时会为每一个data中的属性创建一个Dep实例（订阅器），这个是依赖管理器，依赖是什么？指的是该数据相关的所有数据比如computed计算属性、监听器watch等，为什么要收集？因为要知道哪些数据后面可能要更新。</p>
<p>然后在编译器里，会开始寻找模版上的动态数据，比如指令、插值语法什么的，并初始化视图，一旦发现模版上有使用data中的动态数据，此时就会调用数据劫持中的getter函数，这时Dep就会开始收集依赖，同时为每一个依赖创建watcher，它是订阅者，来订阅Dep啥时候通知它数据变化了，它会为依赖绑定一个更新函数，为后续的更新做准备。一旦数据发生变化了，就会触发数据劫持的getter函数，此时Dep就会通知相应的watcher叫它执行相应的更新函数来实现响应式。watcher是怎么更新的，更新的策略是什么？一般来说，它会主动要求调度者scheduler将自己放到一个执行队列中，再下一个事件循环统一进行更新。这么做有以下好处：</p>
<ol start="2">
<li>避免了频繁更新带来的性能问题</li>
</ol>
<p><strong>3、上面只是说了响应式的原理，下面说说编译器又如何让view的变化来影响model的：</strong></p>
<p>主要是通过在编译时对于使用到的事件的处理，通过事件可以让用户的操作影响到model中的数据，然后再通过上面的响应式，这就实现了双向绑定。</p>
</blockquote>
<h3 id="27-了解虚拟DOM和Diff算法吗？虚拟DOM的优缺点是什么？"><a href="#27-了解虚拟DOM和Diff算法吗？虚拟DOM的优缺点是什么？" class="headerlink" title="27.了解虚拟DOM和Diff算法吗？虚拟DOM的优缺点是什么？"></a>27.了解虚拟DOM和Diff算法吗？虚拟DOM的优缺点是什么？</h3><blockquote>
<p>我们手动频繁地去更新DOM是非常消耗性能的，因此就有了虚拟DOM，它是利用js对真实DOM的抽象表示，用来比对新旧节点的不同，针对不同点来更新真实DOM，这样就降低了更新DOM的频率，提高了性能。</p>
<p>而Diff算法就是用来给虚拟DOM比对新旧虚拟DOM的不同的，返回值是一个patch对象，它包含了所有新旧节点的不同之处。</p>
<p>Ps：可以把整个从头到尾的过程说一遍，从 编译-&gt;生成render函数-&gt;生成虚拟DOM-&gt;Diff算法的具体过程</p>
<p>优缺点：</p>
<ul>
<li>优点：减少DOM操作，提高性能</li>
<li>缺点：首次加载大量DOM时，还要进行虚拟DOM的计算，产生额外开销</li>
</ul>
</blockquote>
<h3 id="28-Vue和Jquery的区别是什么？"><a href="#28-Vue和Jquery的区别是什么？" class="headerlink" title="28.Vue和Jquery的区别是什么？"></a>28.Vue和Jquery的区别是什么？</h3><blockquote>
<ol>
<li>定义：Vue是js框架，用于构建交互式的页面；而Jquery是一个库，用于简化操作DOM、事件处理等</li>
<li>着重点：Vue是有双向数据绑定的概念的，数据和视图是自定更新的，因此更注重数据的处理，无需关心DOM的更新；而Jquery需要手动获取DOM并处理更新，更注重DOM的处理</li>
<li>前景：Vue更加流行，而Jquery在慢慢淘汰</li>
</ol>
</blockquote>
<h3 id="29-Vuex的响应式处理是什么？"><a href="#29-Vuex的响应式处理是什么？" class="headerlink" title="29.Vuex的响应式处理是什么？"></a>29.Vuex的响应式处理是什么？</h3><blockquote>
<p>Vuex是共享数据管理的工具。</p>
<p>Vuex的响应式处理其实就是 怎么通过修改state的值从而又更新到视图的，也就是把 修改值-&gt;获取值 的过程说一遍：</p>
<ol>
<li>使用Vue.use(vuex)，调用install方法，install方法内部又调用applyMixin方法（其实就是利用mixin混入）将vuex实例挂载到Vue实例身上，这样在任意组件内都可以使用this.$store来访问了</li>
<li>在Vue组件内去触发dispatch来调用actions的方法，可以异步请求数据存入store，也可以调用mutations来修改state数据</li>
<li>mutations直接修改state中的数据</li>
<li>用getters对state中的数据进行计算处理，方便组件直接使用，不需要额外的计算处理</li>
<li>在组件中把state的数据引入到data中，借助data中数据响应式特点更新到视图中（最关键）</li>
</ol>
</blockquote>
<h3 id="30-Vue中常用到的遍历方法有哪些？"><a href="#30-Vue中常用到的遍历方法有哪些？" class="headerlink" title="30.Vue中常用到的遍历方法有哪些？"></a>30.Vue中常用到的遍历方法有哪些？</h3><blockquote>
<ul>
<li><p>forEach———普通遍历</p>
<p>元素处理</p>
</li>
<li><p>filter————-过滤元素</p>
</li>
<li><p>reduce———-累加器</p>
</li>
<li><p>map————-数据处理</p>
<p>元素查找和判断</p>
</li>
<li><p>findIndex——返回返回满足条件的第一个元素索引</p>
</li>
<li><p>some————有一个满足则为true</p>
</li>
<li><p>every————全部满足才为true</p>
</li>
</ul>
</blockquote>
<h3 id="31-如何封装一个可复用的组件来使用？"><a href="#31-如何封装一个可复用的组件来使用？" class="headerlink" title="31.如何封装一个可复用的组件来使用？"></a>31.如何封装一个可复用的组件来使用？</h3><blockquote>
<ol>
<li>使用Vue.extends()创建一个组件</li>
<li>使用Vue.components()注册组件</li>
</ol>
<p>组件设计原则：</p>
<ul>
<li>组件原子化，一个组件只干一件事</li>
<li>并且干的事情，分工明确，比如 容器组件管数据（一般是父组件），展示组件管视图（一般是子组件）</li>
</ul>
</blockquote>
<h3 id="32-封装一个可复用的组件需要注意什么？"><a href="#32-封装一个可复用的组件需要注意什么？" class="headerlink" title="32.封装一个可复用的组件需要注意什么？"></a>32.封装一个可复用的组件需要注意什么？</h3><blockquote>
<p>除了上面的设计原则以外，这个可复用的组件要分父组件还是子组件（数据和事件的处理）：</p>
<ul>
<li>作为父组件来复用：<ol>
<li>把事件写在父组件中，子组件只负责触发事件，低耦合</li>
</ol>
</li>
<li>作为子组件来复用：<ol>
<li>不要在子组件请求数据，子组件一般只用来展示UI，况且要多次使用要避免多次请求数据，可以放在父组件请求</li>
<li>对于第一点，取而代之的是提供props来接受数据，数据流明确，便于维护</li>
</ol>
</li>
</ul>
</blockquote>
<h3 id="33-Vue的过滤器怎么使用？"><a href="#33-Vue的过滤器怎么使用？" class="headerlink" title="33.Vue的过滤器怎么使用？"></a>33.Vue的过滤器怎么使用？</h3><blockquote>
<p>Vue的过滤器 其实 就是 angular中的 管道，主要就是针对 文字进行处理，Vue中过滤器有以下两种：</p>
<ul>
<li><p>全局过滤器 ———– 任何组件都可以使用</p>
<figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ada">Vue.filter(<span class="hljs-symbol">&#x27;add</span>&#x27;,<span class="hljs-keyword">function</span>(v)&#123; <span class="hljs-comment">-- 全局添加一个名为add的过滤器，小于10，则添加0</span><br>       <span class="hljs-keyword">return</span> v &lt; <span class="hljs-number">10</span> ? <span class="hljs-string">&#x27;0&#x27;</span>+v : <span class="hljs-type">v</span><br>&#125;)<br></code></pre></td></tr></table></figure></li>
<li><p>本地过滤器 ———–当前组件使用</p>
<p>和methods同级</p>
<figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ada">filters:&#123; <span class="hljs-comment">------------------------------------------- 不止一个过滤器</span><br>       add:<span class="hljs-keyword">function</span>(v)&#123; <br>     <span class="hljs-keyword">return</span> v &lt; <span class="hljs-number">10</span> ? <span class="hljs-string">&#x27;0&#x27;</span>+v : <span class="hljs-type">v</span><br>     &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
</ul>
<p>用法：</p>
<figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="hljs-template-variable">&#123;&#123; <span class="hljs-name">data</span> | add&#125;&#125;</span><br></code></pre></td></tr></table></figure>

<p>注意：过滤器在Vue3中已经废除，因为 我们不希望在插值语法的形式太过复杂，不美观，推荐使用computed计算属性 ————- Vue3和Vue2的不同要好好记忆，很可能因为这个题被问到</p>
</blockquote>
<h3 id="34-Vue中如何做强制刷新？"><a href="#34-Vue中如何做强制刷新？" class="headerlink" title="34.Vue中如何做强制刷新？"></a>34.Vue中如何做强制刷新？</h3><blockquote>
<ul>
<li>location.reload() ————- 浏览器的API，强制重新请求服务器资源，类似于history模式下手动刷新页面</li>
<li>this.router.go(0) ————- 路由器的API，只会重新加载当前组件，不会重新请求服务器</li>
</ul>
</blockquote>
<h3 id="35-Vue3对于Vue2有什么不同"><a href="#35-Vue3对于Vue2有什么不同" class="headerlink" title="35.Vue3对于Vue2有什么不同?"></a>35.Vue3对于Vue2有什么不同?</h3><blockquote>
<p>说一两个当然容易，最重要的是要有条理地全部说出来，以下是思路：</p>
<p>入口文件-&gt;单个组件【Vue实例（生命周期函数-&gt;setup函数-&gt;变量（响应式数据（深浅响应式、与非响应式的转化））-&gt;组合式API（computed和watch）-&gt;自定义API-&gt;传递数据（provide/inject））-&gt;HTML模板（Fargment-&gt;Suspend-&gt;teleport）-&gt;Style样式（动画）】</p>
<p>详细总结链接：<a target="_blank" rel="noopener" href="https://zhoujielovesinging.gitee.io/blog/2023/08/30/Vue3%E7%9B%B8%E5%AF%B9%E4%BA%8EVue2%E7%9A%84%E4%B8%8D%E5%90%8C%E7%94%A8%E6%B3%95/">https://zhoujielovesinging.gitee.io/blog/2023/08/30/Vue3%E7%9B%B8%E5%AF%B9%E4%BA%8EVue2%E7%9A%84%E4%B8%8D%E5%90%8C%E7%94%A8%E6%B3%95/</a></p>
</blockquote>
<h3 id="36-Vue的性能优化有哪些？"><a href="#36-Vue的性能优化有哪些？" class="headerlink" title="36.Vue的性能优化有哪些？"></a>36.Vue的性能优化有哪些？</h3><blockquote>
<p><strong>组件实例</strong></p>
<ul>
<li>按需第三方插件，比如加载UI组件模块</li>
<li>静态资源使用CDN加载（CDN会把静态资源缓存在各个服务器中，查找时会寻找最近的服务器寻找），例如用CDN加载图标库</li>
<li>不要把太多的数据放在data中，减少过多的数据劫持添加getter、setter</li>
<li>对象的层级不要太多，也是减少深层次过多的数据劫持</li>
<li>对于一些不用的变量（比如定时器），要及时清理</li>
<li>computed和watch要区分场景使用，比如computed的缓存性能不适合频繁修改数据的</li>
<li>使用防抖和节流来避免频繁触发事件</li>
<li>v-for给子元素绑定事件要使用事件委托，绑定一次即可</li>
</ul>
<p><strong>模板渲染</strong></p>
<ul>
<li>图片懒加载</li>
<li>图片多使用精灵图和SVG格式的（两者区别见上面）</li>
<li>区分v-if、v-show的使用场景，避免v-if频繁渲染节点</li>
<li>v-for要使用唯一的id并且不要和v-if一起使用（会先执行v-for先渲染再判断导致重复渲染），避免重复渲染</li>
<li>大数据的列表和表格要使用 虚拟列表、虚拟表格技术，可以实现看多少加载多少，翻页时再加载后面的</li>
</ul>
<p><strong>组件</strong></p>
<ul>
<li>使用异步组件（使用component组件渲染时再加载）</li>
<li>使用keep-alive组件</li>
</ul>
<p><strong>路由</strong></p>
<ul>
<li>使用路由懒加载（使用import和webpack代码分割）</li>
<li>使用动态路由</li>
</ul>
<p><strong>打包</strong></p>
<ul>
<li>减少文件体积（合并文件）</li>
<li>使用服务器端gzip压缩（可以使得静态文件体积减少50%-70%，可以用于webpack打包后的静态文件进一步压缩）</li>
</ul>
<p><strong>运行时</strong></p>
<ul>
<li>使用服务端渲染SSR或预渲染（第一次加载）</li>
<li>缓存：客户端缓存或服务端缓存 资源文件（后续加载）</li>
</ul>
<p><u>思路</u>：编程时（组件实例-&gt;HTML模板-&gt;组件级别-&gt;路由级别）-&gt;打包时-&gt;运行后</p>
</blockquote>
<h3 id="37-首屏如何优化？"><a href="#37-首屏如何优化？" class="headerlink" title="37.首屏如何优化？"></a>37.首屏如何优化？</h3><blockquote>
<p>由于问的是首屏，那么只需关注首屏加载用到的资源文件以及渲染内容，因此要去除一些 上一题性能优化中对于编程时提到的操作，直接关注打包后的资源以及运行的渲染即可。首屏优化分为 用户体验优化和加载速度优化。</p>
<ol>
<li><p>使用骨架图（指标不住本，仅仅提高用户体验）</p>
<p>骨架图是 数据还没到来时显示的页面 大致结构的样式，相比loading样式，用户体验更好</p>
</li>
<li><p>优化加载速度（从根本解决问题）</p>
<ul>
<li><p>打包</p>
<p>使用服务器端gzip压缩（可以使得静态文件体积减少50%-70%，可以用于webpack打包后的静态文件进一步压缩）打包后的静态文件</p>
</li>
<li><p>服务端渲染（有了SSR就不需要客户端处理js文件直接渲染）</p>
</li>
<li><p>加载js文件</p>
<p>静态资源使用CDN加载（CDN会把静态资源缓存在各个服务器中，查找时会寻找最近的服务器寻找），例如用CDN加载图标库</p>
</li>
<li><p>路由</p>
<ul>
<li>使用路由懒加载（使用import和webpack代码分割）</li>
<li>使用动态路由</li>
</ul>
</li>
<li><p>组件（页面加载时）</p>
<ul>
<li>非首屏组件异步加载</li>
<li>不重要的首屏组件延迟加载</li>
</ul>
</li>
<li><p>页面渲染</p>
<ul>
<li>图片懒加载</li>
<li>图片多使用精灵图和SVG格式的（两者区别见上面）</li>
<li>区分v-if、v-show的使用场景，避免v-if频繁渲染节点</li>
<li>v-for要使用唯一的id并且不要和v-if一起使用（会先执行v-for先渲染再判断导致重复渲染），避免重复渲染</li>
<li>大数据的列表和表格要使用 虚拟列表、虚拟表格技术，可以实现看多少加载多少，翻页时再加载后面的</li>
</ul>
</li>
</ul>
</li>
</ol>
<p><u>思路</u>：从打包后加载页面的视角来看</p>
</blockquote>
<h3 id="38-为什么Vue3比Vue2性能更好？"><a href="#38-为什么Vue3比Vue2性能更好？" class="headerlink" title="38.为什么Vue3比Vue2性能更好？"></a>38.为什么Vue3比Vue2性能更好？</h3><blockquote>
<p>将从编码、编译、打包三个方面来回答：</p>
<p>编码方面：</p>
<ul>
<li>主要是针对响应式处理进行了改进，由原先的Object.defineProperty变为ES6的Proxy来实现，由于Vue3中的proxy在初始化时使用了懒处理，即嵌套对象一开始指针对第一层进行响应式处理，只有到后面用到深层次的内容再处理，这样可以减少体积和提高加载速度</li>
</ul>
<p>编译方面：</p>
<ul>
<li>添加静态标记（每种标记表明更新不同内容，静态的不更新，动态的有选择地更新）、静态提升（将静态内容提升到render函数之外，在render函数里面只需对静态内容引用，避免重复处理）提升diff算法的性能</li>
<li>事件监听可以进行缓存（比如click函数内容的复用，不用每次点击都要绑定）</li>
</ul>
<p>打包方面：</p>
<ul>
<li>使用Tree Shaking对代码打包时去除不使用的代码，从而减小打包体积</li>
</ul>
</blockquote>
<h3 id="39-Vue3为什么要使用proxy（proxy比Object-defineProperty有什么优点）"><a href="#39-Vue3为什么要使用proxy（proxy比Object-defineProperty有什么优点）" class="headerlink" title="39.Vue3为什么要使用proxy（proxy比Object.defineProperty有什么优点）"></a>39.Vue3为什么要使用proxy（proxy比Object.defineProperty有什么优点）</h3><blockquote>
<ul>
<li><p>范围更广：</p>
<p>proxy能代理整个对象，而Object.defineProperty则只能代理某个属性，还需要递归来处理，proxy内部已经进行了递归处理</p>
</li>
<li><p>响应式操作更丰富：</p>
<p>Object.defineProperty只能针对数据的访问、修改响应式处理，而proxy还对删除、添加操作添加了响应式</p>
</li>
<li><p>异常处理更灵活：</p>
<p>proxy内部的Relect封装了try-catch，可自动捕获异常，而Object.defineProperty需要手动在外部添加</p>
</li>
<li><p>更安全：</p>
<p>proxy返回一个新对象，而Object.defineProperty是在原对象进行处理的，这样proxy不修改原数据结构会更加安全</p>
</li>
</ul>
<p><u>思路</u>：proxy对象由 外（范围）-&gt;内（操作-异常处理）-&gt;外（返回新对象）</p>
</blockquote>
<h3 id="40-说说你对组件的理解"><a href="#40-说说你对组件的理解" class="headerlink" title="40.说说你对组件的理解"></a>40.说说你对组件的理解</h3><blockquote>
<p>定义：组件其实是利用Vue.extend创建的一个VueComponent实例，类似于Vue的子类，拥有Vue类似的配置项</p>
<p>作用：可以将公共部分写在组件里面进行复用</p>
<p><img src="/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/%E7%BB%84%E4%BB%B6%E5%AE%9E%E4%BE%8B%E4%B8%8EVue%E5%AE%9E%E4%BE%8B%E5%85%B3%E7%B3%BB.png" srcset="/blog/img/loading.gif" lazyload alt="组件实例与Vue实例关系"></p>
</blockquote>
<h3 id="41-为什么组件的data要写成函数形式不能写成对象形式？"><a href="#41-为什么组件的data要写成函数形式不能写成对象形式？" class="headerlink" title="41.为什么组件的data要写成函数形式不能写成对象形式？"></a>41.为什么组件的data要写成函数形式不能写成对象形式？</h3><blockquote>
<p>如果data写成对象形式，那么同一个组件在复用时，data都指向同一个地址，这样data内容一个修改了，另一个也会跟着修改，这样会造成数据混乱的情况。</p>
<p>而写成函数形式，每次都返回一个新的对象，这样可以保证数据的独立性，不会相互影响</p>
</blockquote>
<h3 id="42-你是如何规划项目文件的？"><a href="#42-你是如何规划项目文件的？" class="headerlink" title="42.你是如何规划项目文件的？"></a>42.你是如何规划项目文件的？</h3><blockquote>
<p>项目创建后大致结构已经确定，主要是针对src目录进行规划：</p>
<p>src</p>
<ul>
<li>components（公共组件）</li>
<li>views（路由组件）</li>
<li>utils（封装axios）</li>
<li>api</li>
<li>store</li>
<li>assets（图标、图片）</li>
<li>directives（可选，自定义指令）</li>
<li>styles（自定义样式）</li>
<li>router</li>
</ul>
<p><u>思路</u>：组件-&gt;组件实例（请求数据）-&gt;模板展示（图片、指令）-&gt;样式-&gt;路由（不同组件）</p>
</blockquote>
<h3 id="42-了解过nuxt-js框架吗？"><a href="#42-了解过nuxt-js框架吗？" class="headerlink" title="42.了解过nuxt.js框架吗？"></a>42.了解过nuxt.js框架吗？</h3><blockquote>
<p>它是一个基于Vue的服务端渲染框架，能够简化服务端渲染流程，最终可帮助SEO（主要目的）以及优化首屏加载时间。</p>
</blockquote>
<h3 id="43-Vue项目中如何提高SEO？"><a href="#43-Vue项目中如何提高SEO？" class="headerlink" title="43.Vue项目中如何提高SEO？"></a>43.Vue项目中如何提高SEO？</h3><blockquote>
<ul>
<li>服务端渲染（SSR）</li>
<li>预渲染：服务端将页面中的静态内容渲染成静态HTML页面返回给客户端，降低客户端渲染负担，但是不太适合频繁更新的页面</li>
</ul>
<p>理解：预渲染只针对静态内容，而SSR是针对所有内容，都是服务端来帮助客户端减轻压力的</p>
</blockquote>
<h3 id="44-为什么Vue2响应式属性要通过额外的API？"><a href="#44-为什么Vue2响应式属性要通过额外的API？" class="headerlink" title="44.为什么Vue2响应式属性要通过额外的API？"></a>44.为什么Vue2响应式属性要通过额外的API？</h3><blockquote>
<p><strong>对象</strong></p>
<ol>
<li>Object.defineProperty无法劫持新增的属性以及删除的属性</li>
<li>如果用Object.defineProperty遍历对象的每个属性添加响应式，效率会很低；而新增API可以手动的有选择性地添加相应是</li>
</ol>
<p><strong>数组</strong></p>
<ol>
<li>通过索引访问值和修改数组长度是无法触发响应式更新</li>
</ol>
</blockquote>
<h3 id="45-为什么Vue对于数组没有使用defineProperty-实现响应式？"><a href="#45-为什么Vue对于数组没有使用defineProperty-实现响应式？" class="headerlink" title="45.为什么Vue对于数组没有使用defineProperty 实现响应式？"></a>45.为什么Vue对于数组没有使用defineProperty 实现响应式？</h3><blockquote>
<p>如果对数组每一个元素都是用defineProperty进行数据劫持，性能会很低下，于是重写了7个方法，<strong>在方法内部调用Array原始方法之后再统一notify更新</strong></p>
</blockquote>
<h3 id="46-Vue使用了哪些设计模式？"><a href="#46-Vue使用了哪些设计模式？" class="headerlink" title="46.Vue使用了哪些设计模式？"></a>46.Vue使用了哪些设计模式？</h3><blockquote>
<ol>
<li>单例模式：vuex和vue router创建后会判断只有一个实例才会创建</li>
<li>观察者模式：响应式原理</li>
<li>发布订阅模式：$emit以及$on，还有promise原理</li>
<li>装饰器模式：@写在方法上面添加额外功能，一般对于防抖和节流可以封装成装饰器来使用</li>
<li>策略模式：对于minin混入的代码，有默认的混入策略：用引进来的替换原本的（如果重名的话）</li>
<li>工厂模式：根据参数生成实例，例如Vue.component(…)</li>
</ol>
<p><u>思路</u>：组件实例-&gt;引入文件-&gt;数据-&gt;事件-&gt;引入外部-&gt;整个组件</p>
</blockquote>
<h3 id="47-MVC和MVVM的区别"><a href="#47-MVC和MVVM的区别" class="headerlink" title="47.MVC和MVVM的区别"></a>47.<strong>MVC和MVVM的区别</strong></h3><blockquote>
<p>MVC它是传统的架构模式，一般是后端来用的。MVC是指Model、View、Controller三层，用户在VieW层请求数据，Controller负责转发用户的请求并把数据分发给Model中，最后由Model将数据再展示到View中，这是一个三角形闭环。</p>
<p>说到MVVM，需要说一下它的由来。</p>
<p>以前我们是将业务层逻辑（从Controller获取数据）还有展示逻辑（利用jquery）都嵌入在页面中，随着的项目的越来越复杂，代码会变得非常地臃肿，可维护性很差。这时就出现了MVVM架构模式。MVVM是前端的架构模式，分为Model、View、ViewModel，ViewModel实现了Model和View的双向绑定，它是没有像MVC的Controller那样有处理业务逻辑这一层的，它更专注于View层，而且Model和View之间是相互独立的，非常低耦合，Model和View并不知道它们的关系，这个关系交给ViewModel来负责，数据一到Model就会自动更新到View中。这样的模式不仅可以将前端页面和后端逻辑分开，各司其职，还可以做到自动更新，大大提高了开发效率。 </p>
<p>注意：Vue并没有完全遵循MVVM模式，因为Vue允许用$ref来操作view</p>
</blockquote>
<h3 id="48-Vue响应式原理中wacher是如何更新页面的（nextTick原理）？"><a href="#48-Vue响应式原理中wacher是如何更新页面的（nextTick原理）？" class="headerlink" title="48.Vue响应式原理中wacher是如何更新页面的（nextTick原理）？"></a>48.Vue响应式原理中wacher是如何更新页面的（nextTick原理）？</h3><blockquote>
<p>watcher它是把自己放在一个更新队列中，并且利用nextTick机制在下一次事件循环统统一针对更新对立进行更新，这样可以只进行一次DOM更新操作，避免重复DOM操作，提高了性能</p>
<p>nextTick机制是如何实现在下一次事件循环的tick才执行回调（在DOM更新完才执行回调）？</p>
<p>它是 将回调函数放入微任务队列去执行 实现的，当前主线程执行完，当前事件循环的DOM也更新完毕，这时再去执行微任务队列中的回调（注意是下一次事件循环的tick而不是整个时间循环）</p>
</blockquote>
<h3 id="49-Vue中有哪些指令？"><a href="#49-Vue中有哪些指令？" class="headerlink" title="49.Vue中有哪些指令？"></a>49.Vue中有哪些指令？</h3><blockquote>
<p><strong>渲染</strong></p>
<ul>
<li><p>编译阶段</p>
<p>v-pre ———不编译</p>
<p>v-cloak —–编译时显示的内容，避免页面闪动</p>
</li>
<li><p>展示阶段</p>
<p>v-show </p>
<p>v-for</p>
</li>
<li><p>展示后</p>
<p>v-once —–只展示一次</p>
</li>
</ul>
<p><strong>数据</strong></p>
<ul>
<li><p>固定HTML内容</p>
<p>v-text —– 只展示最终文字</p>
<p>v-html —- 一股脑地把HTML全部插入</p>
</li>
<li><p>data中数据</p>
<p>v-bind </p>
<p>v-model </p>
</li>
</ul>
<p><strong>事件</strong></p>
<p>v-on</p>
</blockquote>
<h3 id="50-怎样理解-Vue-的单项数据流？"><a href="#50-怎样理解-Vue-的单项数据流？" class="headerlink" title="50.怎样理解 Vue 的单项数据流？"></a>50.怎样理解 Vue 的单项数据流？</h3><blockquote>
<p>它指的是 数据总是从父组件传入子组件，而不允许子组件修改父组件的数据，这么做的目的是 避免子组件意外修改父组件的数据带来的问题</p>
<p>其中，虽然v-model可以修改，但是不推荐，开发环境也会报警告</p>
<p>如何处理？利用$emi触发事件来修改</p>
</blockquote>
<h3 id="51-v-if-和-v-for-为什么不建议一起使用？"><a href="#51-v-if-和-v-for-为什么不建议一起使用？" class="headerlink" title="51.v-if 和 v-for 为什么不建议一起使用？"></a>51.v-if 和 v-for 为什么不建议一起使用？</h3><blockquote>
<p>因为 v-for会在v-if之前解析，如果 v-if 条件不满足，那么又要销毁元素，这样就很影响性能</p>
<p>如何解决？</p>
<p>用计算属性计算 替换 v-if 判断逻辑，提前把数据筛选好即可</p>
</blockquote>
<h3 id="52-v-model原理？"><a href="#52-v-model原理？" class="headerlink" title="52.v-model原理？"></a>52.v-model原理？</h3><blockquote>
<p>v-model他只是v-bind和v-on的语法糖，v-bind实现了数据驱动试图，v-on实现了视图驱动数据，最终实现了双向数据绑定。</p>
<p>它会根据不同的元素标签使用不同的绑定的数据类型以及事件类型：</p>
<ul>
<li>text/textarea——– value值和input事件</li>
<li>select—————— value值和change事件</li>
<li>radio——————- checked值和change事件</li>
</ul>
</blockquote>
<h3 id="53-事件绑定原理？"><a href="#53-事件绑定原理？" class="headerlink" title="53.事件绑定原理？"></a>53.事件绑定原理？</h3><blockquote>
<p>可以对 双向数据绑定的原理 进行补充</p>
<p>需要先说明事件分类：原生事件、组件事件</p>
<p>原生事件是通过addEventListener方法实现的，而组件事件是通过$on实现的。</p>
<p>关系：如果需要在组件上使用原生事件，需要事件修饰符.native</p>
<p>下面说说组件事件用得到$on和$emit原理（原生事件是自带的，不好说）：$on和$emit维护了一个事件中心，使用了发布订阅者模式，$on是订阅者，在$on的时候将事件名放入事件中心，等待订阅内容，而$emit是发布者，在$emit的时候提供$on需要的事件</p>
</blockquote>
<h1 id="Echarts"><a href="#Echarts" class="headerlink" title="Echarts"></a>Echarts</h1><h3 id="1-echarts有用过吗？常用的组件有哪些？"><a href="#1-echarts有用过吗？常用的组件有哪些？" class="headerlink" title="1.echarts有用过吗？常用的组件有哪些？"></a>1.echarts有用过吗？常用的组件有哪些？</h3><blockquote>
<ol>
<li>legend（图例）</li>
<li>title</li>
<li>toolbox</li>
<li>yAxis</li>
<li>xAxis</li>
<li>series（name、type、data、markPoint、markLine）</li>
<li>toolTip</li>
</ol>
<p><u>思路</u>（折线图为例）：</p>
<ul>
<li>外框：从上往下，从左往右（图例-&gt;标题-&gt;工具栏-&gt;Y轴-&gt;X轴）</li>
<li>内容：画出来再对比（哪根线-&gt;类型-&gt;数据-&gt;标注-&gt;标线-&gt;提示框）</li>
</ul>
</blockquote>
<h1 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h1><h3 id="1-前端中产品的性能是指什么？如何衡量？"><a href="#1-前端中产品的性能是指什么？如何衡量？" class="headerlink" title="1.前端中产品的性能是指什么？如何衡量？"></a>1.前端中产品的性能是指什么？如何衡量？</h3><blockquote>
<p>当我们用性能去描述一个互联网产品的时候，主要看的是：</p>
<ul>
<li>首屏加载速度</li>
<li>用户操作流畅度（稳定性）</li>
<li>用户操作响应速度</li>
</ul>
<p>由于速度的快慢用个人主观感受来看因人而异，无法测量，因此业内制定了专业客观的、可测量的性能指标（如下）</p>
</blockquote>
<h3 id="2-性能优化指标有哪些？"><a href="#2-性能优化指标有哪些？" class="headerlink" title="2.性能优化指标有哪些？"></a>2.性能优化指标有哪些？</h3><blockquote>
<ul>
<li><p>首屏加载速度</p>
<p>FCP（页面加载到第一个元素渲染成功的时间）</p>
<p>LCP（页面加载到最大的元素渲染成功的时间）</p>
</li>
<li><p>用户操作流畅度</p>
<p>CLS（累计布局偏移）：如果应用不稳定，页面布局是会慢慢偏移的</p>
</li>
<li><p>用户操作响应速度</p>
<p>FID（First Input delay）：第一次交互并成功相应的时间</p>
</li>
</ul>
</blockquote>
<h3 id="3-性能优化分析工具有哪些？"><a href="#3-性能优化分析工具有哪些？" class="headerlink" title="3.性能优化分析工具有哪些？"></a>3.性能优化分析工具有哪些？</h3><blockquote>
<p>最主流：lighthouse（谷歌插件，要有外网权限）</p>
<p>webVitals：Edge可以用，可以直接挂在网页上，网页处理完毕，直接打开看所有参数，很方便</p>
</blockquote>
<h3 id="4-虚拟表格技术如何实现？"><a href="#4-虚拟表格技术如何实现？" class="headerlink" title="4.虚拟表格技术如何实现？"></a>4.虚拟表格技术如何实现？</h3><blockquote>
<p>就是通过在有限的窗口自定义想要渲染的item条数，然后在滚动过程中通过计算来替换视野中的数据</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">相对定位</span>;<span class="hljs-attr">overflow:auto</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&#x27;height:$&#123;想要展示的个数*一个item高度&#125;&#x27;</span>&gt;</span>// 可视区<br>    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&#x27;height:$&#123;实际个数*一个item高度&#125;&#x27;</span>&gt;</span>// 占位，为了显现出滚动条<br>        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">绝对定位</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&#x27;top:$&#123;第一个元素离top偏移量&#125;&#x27;</span>&gt;</span>// 渲染区域<br>            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> &gt;</span>// 每一行<br>                循环展示<br>            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>下面是滚动时的事件，替换元素：计算第一个元素新下标和偏移量(滚动时第一个元素有偏移量)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> <span class="hljs-title function_">strollEvent</span> = (<span class="hljs-params">event</span>)=&gt;&#123;<br>    <span class="hljs-comment">// 利用节流节省性能</span><br>    <span class="hljs-keyword">if</span>(<span class="hljs-title function_">nowTime</span>() - lastTime)&#123;<br>         <span class="hljs-keyword">const</span> &#123;strollTop&#125; = event.<span class="hljs-property">target</span><br>         第一个元素新下标 = strollTop/一个item高度<br>         第一个元素偏移量 = strollTop<br>         更新lastTime<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>渲染个数少、节流 =&gt;双重提高性能</p>
</blockquote>
<h1 id="移动端"><a href="#移动端" class="headerlink" title="移动端"></a>移动端</h1><h3 id="1-什么是uni-app？"><a href="#1-什么是uni-app？" class="headerlink" title="1.什么是uni-app？"></a>1.什么是uni-app？</h3><blockquote>
<p>uni-app（unify）是一个基于Vue.js的跨平台开发框架（支持Web端、移动端（IOS、android）、小程序），它提供了一套统一的代码开发和发布流程，开发者只需开发一份代码，同时将应用发布到多个平台，大大提高了开发效率。</p>
</blockquote>
<h3 id="2-uni-app有没有做过分包？"><a href="#2-uni-app有没有做过分包？" class="headerlink" title="2.uni-app有没有做过分包？"></a>2.uni-app有没有做过分包？</h3><blockquote>
<p>uni-app分包和webpack的按照模块来代码分割 概念是类似的。</p>
<p>举例子：小程序会默认下载主包并启动页面，只有加载到分包的内容时才会下载对应的分包并加载</p>
<p>好处：提高下载速度和页面加载速度</p>
<p>如何分包：</p>
<p>subPackages里面配置 分包路径以及分包页面</p>
</blockquote>
<h3 id="3-如何利用Cordova对Vue项目进行打包？"><a href="#3-如何利用Cordova对Vue项目进行打包？" class="headerlink" title="3.如何利用Cordova对Vue项目进行打包？"></a>3.如何利用Cordova对Vue项目进行打包？</h3><blockquote>
<p>安装Cordova-&gt;创建一个Cordova项目-&gt;Cordova添加平台-&gt;将Vue在build之后的静态文件放入www文件夹-&gt;cordova run android</p>
</blockquote>
<h3 id="3-原生App、WebApp、混合App、h5有什么区别？"><a href="#3-原生App、WebApp、混合App、h5有什么区别？" class="headerlink" title="3.原生App、WebApp、混合App、h5有什么区别？"></a>3.原生App、WebApp、混合App、h5有什么区别？</h3><blockquote>
<p><strong>原生App：</strong></p>
<p>​      所有的api还有UI一整套都是用的android/ios官方提供的</p>
<p>​      优点：系统稳定，流畅度高</p>
<p>​      缺点：无法跨平台</p>
<p><strong>WebApp：</strong></p>
<p>​      用的是Web应用开发的技术（html、css、js）开发的移动端应用</p>
<p>​      优点：可跨平台</p>
<p>​      缺点：无法调用原生App的接口</p>
<p><strong>混合App（uniapp、cordova）：</strong></p>
<p>​      综合了原生App和混合App优点，一部分用WebApp的技术，一部分用原生App的接口</p>
<p><strong>h5</strong></p>
<p>​      单纯用html5技术开发的应用，更注重动画和交互效果，web和app都可以运行</p>
</blockquote>
<h1 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h1><h3 id="1-git从拉取代码到推送代码的流程是什么？"><a href="#1-git从拉取代码到推送代码的流程是什么？" class="headerlink" title="1.git从拉取代码到推送代码的流程是什么？"></a>1.git从拉取代码到推送代码的流程是什么？</h3><blockquote>
<p>git clone 远程仓库地址</p>
<p>-&gt;…修改内容</p>
<p>-&gt;git add .（添加到本地缓存区）</p>
<p>-&gt;git commit -m ‘feature/内容’（添加到本地仓库）</p>
<p>-&gt;git pull </p>
<p>-&gt;…处理可能的冲突</p>
<p>-&gt;git push </p>
<hr>
<p>以上是个人项目或小型项目，如果是大型项目，会要建立自己的分支，有效避免pull、commit的使用错误（相当于用merge来代替pull）：</p>
<p>git clone 远程仓库地址</p>
<p>-&gt;git checkout -b ‘自己的分支’（创建并切换到自己的分支）</p>
<p>-&gt;add、commit</p>
<p>-&gt;替换到master再pull一下保证只最新的</p>
<p>-&gt;再切换到自己的分支</p>
<p>-&gt;git merge develop（将自己分支的内容合并到develop分支上）</p>
</blockquote>
<h3 id="2-会什么要有暂存区？"><a href="#2-会什么要有暂存区？" class="headerlink" title="2.会什么要有暂存区？"></a>2.会什么要有暂存区？</h3><blockquote>
<p>因为暂存区可以帮助你选择性提交，一次提交只包含一个功能，这样可以让提交历史清晰，便于追踪问题。如果你要做一个功能B，但是在做的过程中发现还有bugA，此时可以把修改bugA先提交一版，然后再提交一版功能B</p>
</blockquote>
<h3 id="3-为什么是commit-gt-pull-gt-push，而不是pull-gt-commit-gt-push？"><a href="#3-为什么是commit-gt-pull-gt-push，而不是pull-gt-commit-gt-push？" class="headerlink" title="3.为什么是commit-&gt;pull-&gt;push，而不是pull-&gt;commit-&gt;push？"></a>3.为什么是commit-&gt;pull-&gt;push，而不是pull-&gt;commit-&gt;push？</h3><blockquote>
<p>因为可能 你和别人共同会修改同一个功能，你可能修改好了忘记commit直接pull，会把别人修改的覆盖你的，因为你不commit那么git是不知道你修改了；而先commit再pull，这是如果有冲突，可以和别人商量要哪个，再push</p>
</blockquote>
<h3 id="4-A合并B和B合并A，有什么区别？"><a href="#4-A合并B和B合并A，有什么区别？" class="headerlink" title="4.A合并B和B合并A，有什么区别？"></a>4.A合并B和B合并A，有什么区别？</h3><blockquote>
<p>A merge B是将A的内容合并到B中，B merge A是将B的内容合并到A中。</p>
<p>A merge B后会显示merge branch A into B</p>
</blockquote>
<h3 id="5-功能写错了并push了，如何回退代码？"><a href="#5-功能写错了并push了，如何回退代码？" class="headerlink" title="5.功能写错了并push了，如何回退代码？"></a>5.功能写错了并push了，如何回退代码？</h3><blockquote>
<p>一般来说需要回退到 commit之前，然后重新修改，再次commit。</p>
<ul>
<li>git reset –soft HEAD^ ————– 回退到上一次commit之前</li>
<li>git reset –soft commitId ———- 根据commitId ，回退到具体某次提交</li>
</ul>
</blockquote>
<h1 id="HR"><a href="#HR" class="headerlink" title="HR"></a>HR</h1><h3 id="1-你跳槽（离职）的原因是什么？"><a href="#1-你跳槽（离职）的原因是什么？" class="headerlink" title="1.你跳槽（离职）的原因是什么？"></a>1.你跳槽（离职）的原因是什么？</h3><blockquote>
<p><strong>原则：</strong>不要说上家的不好，说一些 不可抗力、个人发展瓶颈 体现上进心的原因<u>（真诚点不要套路）</u>，最后注意要感恩上一家公司的价值</p>
<p><strong>踩雷：</strong></p>
<p>同事相处不和谐 ————–&gt; 沟通能力不行</p>
<p>领导人品不好 —————–&gt; 不尊重领导</p>
<p><strong>方向：</strong> <strong>个人发展 技术提升</strong> 家庭问题</p>
<p><strong>回答</strong>：</p>
<p>主要是有两方面原因：</p>
<p>一方面，前公司确实经营不善，发展前景不太好，因为 公司内部有消息说 公司有面临倒闭的风险，那我肯定要早做打算吧，而且我之前刚毕业的时候本来就是想来 南京/苏州 这样的城市来学习实践的，想来见见世面；另一方面，我们公司做的项目，有很多是用angular框架做的，只有少部分使用Vue框架的，而我不太喜欢用这个框架，因为 我个人觉得这个框架 有点庞大，应用加载速度比较慢，影响开发效率，还有一些用法比如 双向绑定用法复杂，我喜欢用Vue框架开发，更简洁，而且也是主流嘛</p>
</blockquote>
<h3 id="2-工作到现在碰到的最难的问题是什么？怎么解决的？"><a href="#2-工作到现在碰到的最难的问题是什么？怎么解决的？" class="headerlink" title="2.工作到现在碰到的最难的问题是什么？怎么解决的？"></a>2.工作到现在碰到的最难的问题是什么？怎么解决的？</h3><blockquote>
<p><strong>原则：</strong>有一定难度的，并且不常见的</p>
<p><strong>踩雷：</strong></p>
<p>没有问题 —————— 都没做过什么事情，经历少</p>
<p>数据请求、页面渲染、路由请求等常见的应该会的 ————————–技术能力差</p>
<p><strong>思路：</strong></p>
<ol>
<li>一开始自行到处查资料去寻求解决办法</li>
<li>后来实在解决不了的请教同事、组长</li>
</ol>
<p><strong>范本：</strong></p>
<p>“在我的工作经历中，我遇到的最具挑战性的问题是在项目中遇到的性能优化难题。项目中页面加载速度较慢，影响用户体验。为了解决这个问题，我进行了深入的优化工作。首先，我利用开发者工具和性能分析工具对页面进行了详细的性能检测和分析，找到了导致页面加载缓慢的具体原因。然后，我针对性地优化了代码结构，减少了不必要的资源加载和提升了代码执行效率。同时，我还实现了懒加载、缓存和压缩等技术手段，进一步提升了页面加载速度和性能表现。经过持续的努力和测试验证，最终成功地解决了性能优化问题，提升了页面的加载速度，优化了用户体验。这个经历让我深刻意识到在面对困难时，需要耐心、积极主动地寻找解决方案，并不断学习和尝试新的技术，以提升自己的技能水平和解决问题的能力。”</p>
<p>————-这样的回答能够突出问题的难度和挑战性，同时展现了你对问题的认真分析、积极解决的态度和努力付出。这种回答符合面试官希望听到的体现困难、解决积极的标准要求，有助于展现你作为前端工程师的能力和素质。</p>
</blockquote>
<h3 id="3-你的优缺点是什么？"><a href="#3-你的优缺点是什么？" class="headerlink" title="3.你的优缺点是什么？"></a>3.你的优缺点是什么？</h3><blockquote>
<p>我的<strong>缺点</strong>是 我之前胆子比较小（缺点认知），有点怯场，不太敢在大家面前表达自己的观点，后来 我有私下去问资历比较老的同事咨询建议以及看了一些关于表达的一些书籍（非暴力沟通，感受并合理表达自己的需求请求），慢慢建立了自信，后来为了实践锻炼，我能够在百来人号面前进行主持。———— 以前的缺点</p>
<p>项目比较急的时候，有点急于求成，乱了自己的节奏，现在已经在慢慢改正，告知自己 急没有用，心静下来事情就解决了</p>
<p>我的优点是 我觉得我对于Vue框架的应用比较熟练，对原理也有一定了解，对于开发的工作任务的跟进是可以保证的，另外就是我的表达能力 还可以，喜欢与人交流，对于一些问题的处理能够和后端进行及时的沟通</p>
</blockquote>
<h3 id="4-如何协同工作？"><a href="#4-如何协同工作？" class="headerlink" title="4.如何协同工作？"></a>4.如何协同工作？</h3><blockquote>
<p>开发前开会议</p>
<p>利用一些工具（git）</p>
</blockquote>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/blog/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/" class="category-chain-item">面试题</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/blog/tags/%E5%89%8D%E7%AB%AF%E7%BB%BC%E5%90%88/">#前端综合</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>前端高频面试题-基础</div>
      <div>https://jay213.gitee.io/blog/2024/01/03/前端高频面试题-基础/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>Zhou Jie</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2024年1月3日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/blog/2024/02/15/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E8%BF%9B%E9%98%B6/" title="前端高频面试题(进阶)">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">前端高频面试题(进阶)</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/blog/2023/12/18/Vue%E4%B8%AD%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/" title="Vue中的性能优化">
                        <span class="hidden-mobile">Vue中的性能优化</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
  
  
    <article id="comments" lazyload>
      
  <div class="disqus" style="width:100%">
    <div id="disqus_thread"></div>
    
      <script type="text/javascript">
        var disqus_config = function() {
          this.page.url = 'https://jay213.gitee.io/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/';
          this.page.identifier = '/blog/2024/01/03/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9F%BA%E7%A1%80/';
        };
        Fluid.utils.loadComments('#disqus_thread', function() {
          var d = document, s = d.createElement('script');
          s.src = '//' + 'fluid' + '.disqus.com/embed.js';
          s.setAttribute('data-timestamp', new Date());
          (d.head || d.body).appendChild(s);
        });
      </script>
    
    <noscript>Please enable JavaScript to view the comments</noscript>
  </div>


    </article>
  


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/blog/js/events.js" ></script>
<script  src="/blog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/blog/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/blog/js/local-search.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/blog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
